diff options
author | Felix Fietkau <nbd@openwrt.org> | 2009-08-21 22:32:31 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2009-08-21 22:32:31 +0000 |
commit | 75ee3370d9bedff6be7a12431ceef5606252e90a (patch) | |
tree | 0922f89b75edebfce606130ea949a977e60e424b | |
parent | 5076142aa3d85e3d8a0da1531ae61ecc8e90cf99 (diff) | |
download | master-31e0f0ae-75ee3370d9bedff6be7a12431ceef5606252e90a.tar.gz master-31e0f0ae-75ee3370d9bedff6be7a12431ceef5606252e90a.tar.bz2 master-31e0f0ae-75ee3370d9bedff6be7a12431ceef5606252e90a.zip |
add a new target for ubicom32 (compiles, but untested)
SVN-Revision: 17350
-rw-r--r-- | target/linux/ubicom32/Makefile | 27 | ||||
-rwxr-xr-x | target/linux/ubicom32/config-2.6.28 | 169 | ||||
-rw-r--r-- | target/linux/ubicom32/image/Makefile | 12 | ||||
-rw-r--r-- | target/linux/ubicom32/patches-2.6.28/100-ubicom32_support.patch | 48922 |
4 files changed, 49130 insertions, 0 deletions
diff --git a/target/linux/ubicom32/Makefile b/target/linux/ubicom32/Makefile new file mode 100644 index 0000000000..7bdbd204e8 --- /dev/null +++ b/target/linux/ubicom32/Makefile @@ -0,0 +1,27 @@ +# +# Copyright (C) 2006-2008 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk + +#Add target specific (nommu) flags +TARGET_CFLAGS+= -DNO_FORK + +ARCH:=ubicom32 +BOARD:=ubicom32 +BOARDNAME:=Ubicom IPxx +FEATURES:=nommu + +LINUX_VERSION:=2.6.28.10 + +include $(INCLUDE_DIR)/target.mk + +KERNEL_MAKEOPTS += LDFLAGS_BUILD_ID="" + +define Target/Description + Build firmware images for Ubicom based routers and media devices. +endef + +$(eval $(call BuildTarget)) diff --git a/target/linux/ubicom32/config-2.6.28 b/target/linux/ubicom32/config-2.6.28 new file mode 100755 index 0000000000..8c1dae5ddc --- /dev/null +++ b/target/linux/ubicom32/config-2.6.28 @@ -0,0 +1,169 @@ +# CONFIG_ACCESS_OK_CHECKS_ENABLED is not set +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_ARCH_SUPPORTS_AOUT=y +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_ARPD is not set +CONFIG_BASE_SMALL=0 +CONFIG_BINFMT_ELF_FDPIC=y +CONFIG_BINFMT_FLAT=y +# CONFIG_BINFMT_SHARED_FLAT is not set +CONFIG_BINFMT_ZFLAT=y +CONFIG_BITREVERSE=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BRD_64MB=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CLASSIC_RCU=y +CONFIG_CMDLINE="console=ttyUM0 console=ttyUS0 serdes=0x02004000,61,250000000" +CONFIG_CMDLINE_BOOL=y +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CRC_CCITT=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_RNG2=y +# CONFIG_CRYPTO_UBICOM32 is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_IRQMEASURE is not set +# CONFIG_DEBUG_PCIMEASURE is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_IOSCHED="noop" +CONFIG_DEFAULT_NOOP=y +CONFIG_DEVKMEM=y +CONFIG_DEVPORT=y +CONFIG_EARLY_PRINTK=y +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_EPOLL is not set +# CONFIG_EVENTFD is not set +CONFIG_FORCE_MAX_ZONEORDER=14 +# CONFIG_FPU is not set +# CONFIG_FUTEX is not set +# CONFIG_FW_LOADER is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_GPIO=y +# CONFIG_GEN_RTC is not set +CONFIG_GPIOLIB=y +# CONFIG_HAMRADIO is not set +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAVE_CLK=y +# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set +CONFIG_HAVE_OPROFILE=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_UBICOM32=y +# CONFIG_I2C is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_SOURCE="../romfs ../vendors/Ubicom/RouterGateway/initramfs_list" +CONFIG_INPUT=y +# CONFIG_INPUT_MISC is not set +CONFIG_INPUT_POLLDEV=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IP5160DEV is not set +# CONFIG_IP5160EVAL is not set +# CONFIG_IP5170DPF is not set +# CONFIG_IP7145DPF is not set +# CONFIG_IP7160BRINGUP is not set +# CONFIG_IP7160DPF is not set +CONFIG_IP7160RGW=y +# CONFIG_IP7500AV is not set +# CONFIG_IP7500MEDIA is not set +# CONFIG_IP7500MODULE is not set +# CONFIG_IRQSTACKS is not set +CONFIG_IRQ_PER_CPU=y +# CONFIG_ISDN is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +# CONFIG_JFFS2_SUMMARY is not set +CONFIG_KERNELBASE=0x40400000 +CONFIG_LDM_DEBUG=y +CONFIG_LDM_PARTITION=y +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_TRIGGERS is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_LIBCRC32C=y +# CONFIG_LINKER_RELAXATION is not set +# CONFIG_MISC_DEVICES is not set +# CONFIG_MMU is not set +CONFIG_MODULE_FORCE_LOAD=y +# CONFIG_MODULE_UNLOAD is not set +# CONFIG_MTD_CFI is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_UBI32_M25P80=y +# CONFIG_NET_ETHERNET is not set +# CONFIG_NOBOARD is not set +CONFIG_NO_HZ=y +CONFIG_NO_IOPORT=y +# CONFIG_NO_KERNEL_MSG is not set +CONFIG_OCM_MODULES_FALLBACK_TO_DDR=y +CONFIG_OCM_MODULES_MAY_CONSUME_REMAINING_CODESPACE=y +CONFIG_OCM_MODULES_RESERVATION=41 +# CONFIG_PACKET_MMAP is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_PCI=y +CONFIG_PCI_DEV0_IDSEL=0x001000000 +CONFIG_PCI_DEV1_IDSEL=0x002000000 +CONFIG_PRINTK_TIME=y +CONFIG_PROTECT_KERNEL=y +CONFIG_RAMBASE=0x40000000 +CONFIG_RAMKERNEL=y +CONFIG_RAMSIZE=0x4000000 +# CONFIG_RTC is not set +CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y +# CONFIG_SCSI_DMA is not set +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_UBI32_MAILBOX=y +CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE=y +# CONFIG_SERIAL_UBI32_SERDES is not set +# CONFIG_SERIAL_UBI32_UARTTIO is not set +# CONFIG_SIGNALFD is not set +# CONFIG_SMP is not set +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_MASTER=y +# CONFIG_SPI_SPIDEV is not set +CONFIG_SPI_UBICOM32_GPIO=y +# CONFIG_STOP_ON_BUG is not set +# CONFIG_STOP_ON_TRAP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_TICK_ONESHOT=y +# CONFIG_TIMERFD is not set +CONFIG_TIMER_EXTRA_ALLOC=0 +CONFIG_TIME_LOW_RES=y +CONFIG_TINY_SHMEM=y +CONFIG_UBI32_WDT=y +CONFIG_UBICOM32=y +CONFIG_UBICOM32_GMAC=y +CONFIG_UBICOM32_NAPI=y +CONFIG_UBICOM32_OCM_FOR_SKB=y +CONFIG_UBICOM32_V4=y +# CONFIG_UBICOM_HID is not set +CONFIG_UBICOM_INPUT=y +# CONFIG_UBICOM_INPUT_I2C is not set +# CONFIG_UNALIGNED_ACCESS_DISABLED is not set +CONFIG_UNALIGNED_ACCESS_ENABLED=y +# CONFIG_UNALIGNED_ACCESS_USERSPACE_ONLY is not set +# CONFIG_VGASTATE is not set +# CONFIG_WIRELESS_OLD_REGULATORY is not set diff --git a/target/linux/ubicom32/image/Makefile b/target/linux/ubicom32/image/Makefile new file mode 100644 index 0000000000..64b294d76e --- /dev/null +++ b/target/linux/ubicom32/image/Makefile @@ -0,0 +1,12 @@ +# +# Copyright (C) 2006 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/image.mk + +# use default targets for everything + +$(eval $(call BuildImage)) diff --git a/target/linux/ubicom32/patches-2.6.28/100-ubicom32_support.patch b/target/linux/ubicom32/patches-2.6.28/100-ubicom32_support.patch new file mode 100644 index 0000000000..4ec511fa6e --- /dev/null +++ b/target/linux/ubicom32/patches-2.6.28/100-ubicom32_support.patch @@ -0,0 +1,48922 @@ +--- /dev/null ++++ b/arch/ubicom32/crypto/aes_ubicom32.c +@@ -0,0 +1,458 @@ ++/* ++ * arch/ubicom32/crypto/aes_ubicom32.c ++ * Ubicom32 implementation of the AES Cipher Algorithm. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <crypto/aes.h> ++#include <crypto/algapi.h> ++#include <linux/err.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/spinlock.h> ++#include "crypto_ubicom32.h" ++#include <asm/linkage.h> ++ ++struct ubicom32_aes_ctx { ++ u8 key[AES_MAX_KEY_SIZE]; ++ u32 ctrl; ++ int key_len; ++}; ++ ++static inline void aes_hw_set_key(const u8 *key, u8 key_len) ++{ ++ /* ++ * switch case has more overhead than 4 move.4 instructions, so just copy 256 bits ++ */ ++ SEC_SET_KEY_256(key); ++} ++ ++static inline void aes_hw_set_iv(const u8 *iv) ++{ ++ SEC_SET_IV_4W(iv); ++} ++ ++static inline void aes_hw_cipher(u8 *out, const u8 *in) ++{ ++ SEC_SET_INPUT_4W(in); ++ ++ asm volatile ( ++ " ; start AES by writing 0x40(SECURITY_BASE) \n\t" ++ " move.4 0x40(%0), #0x01 \n\t" ++ " pipe_flush 0 \n\t" ++ " \n\t" ++ " ; wait for the module to calculate the output \n\t" ++ " btst 0x04(%0), #0 \n\t" ++ " jmpne.f .-4 \n\t" ++ : ++ : "a" (SEC_BASE) ++ : "cc" ++ ); ++ ++ SEC_GET_OUTPUT_4W(out); ++} ++ ++static int __ocm_text aes_set_key(struct crypto_tfm *tfm, const u8 *in_key, ++ unsigned int key_len) ++{ ++ struct ubicom32_aes_ctx *uctx = crypto_tfm_ctx(tfm); ++ ++ uctx->key_len = key_len; ++ memcpy(uctx->key, in_key, key_len); ++ ++ /* ++ * leave out HASH_ALG (none = 0), CBC (no = 0), DIR (unknown) yet ++ */ ++ switch (uctx->key_len) { ++ case 16: ++ uctx->ctrl = SEC_KEY_128_BITS | SEC_ALG_AES; ++ break; ++ case 24: ++ uctx->ctrl = SEC_KEY_192_BITS | SEC_ALG_AES; ++ break; ++ case 32: ++ uctx->ctrl = SEC_KEY_256_BITS | SEC_ALG_AES; ++ break; ++ } ++ ++ return 0; ++} ++ ++static inline void aes_cipher(struct crypto_tfm *tfm, u8 *out, const u8 *in, u32 extra_flags) ++{ ++ const struct ubicom32_aes_ctx *uctx = crypto_tfm_ctx(tfm); ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ hw_crypto_set_ctrl(uctx->ctrl | extra_flags); ++ ++ aes_hw_set_key(uctx->key, uctx->key_len); ++ aes_hw_cipher(out, in); ++ ++ hw_crypto_unlock(); ++} ++ ++static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) ++{ ++ aes_cipher(tfm, out, in, SEC_DIR_ENCRYPT); ++} ++ ++static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) ++{ ++ aes_cipher(tfm, out, in, SEC_DIR_DECRYPT); ++} ++ ++static struct crypto_alg aes_alg = { ++ .cra_name = "aes", ++ .cra_driver_name = "aes-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_CIPHER, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_aes_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(aes_alg.cra_list), ++ .cra_u = { ++ .cipher = { ++ .cia_min_keysize = AES_MIN_KEY_SIZE, ++ .cia_max_keysize = AES_MAX_KEY_SIZE, ++ .cia_setkey = aes_set_key, ++ .cia_encrypt = aes_encrypt, ++ .cia_decrypt = aes_decrypt, ++ } ++ } ++}; ++ ++static void __ocm_text ecb_aes_crypt_loop(u8 *out, u8 *in, unsigned int n) ++{ ++ while (likely(n)) { ++ aes_hw_cipher(out, in); ++ out += AES_BLOCK_SIZE; ++ in += AES_BLOCK_SIZE; ++ n -= AES_BLOCK_SIZE; ++ } ++} ++ ++static int __ocm_text ecb_aes_crypt(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes, u32 extra_flags) ++{ ++ const struct ubicom32_aes_ctx *uctx = crypto_blkcipher_ctx(desc->tfm); ++ int ret; ++ ++ struct blkcipher_walk walk; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ ret = blkcipher_walk_virt(desc, &walk); ++ if (ret) { ++ return ret; ++ } ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ ++ hw_crypto_set_ctrl(uctx->ctrl | extra_flags); ++ aes_hw_set_key(uctx->key, uctx->key_len); ++ ++ while (likely((nbytes = walk.nbytes))) { ++ /* only use complete blocks */ ++ unsigned int n = nbytes & ~(AES_BLOCK_SIZE - 1); ++ u8 *out = walk.dst.virt.addr; ++ u8 *in = walk.src.virt.addr; ++ ++ /* finish n/16 blocks */ ++ ecb_aes_crypt_loop(out, in, n); ++ ++ nbytes &= AES_BLOCK_SIZE - 1; ++ ret = blkcipher_walk_done(desc, &walk, nbytes); ++ } ++ ++ hw_crypto_unlock(); ++ return ret; ++} ++ ++static int ecb_aes_encrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes) ++{ ++ return ecb_aes_crypt(desc, dst, src, nbytes, SEC_DIR_ENCRYPT); ++} ++ ++static int ecb_aes_decrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes) ++{ ++ return ecb_aes_crypt(desc, dst, src, nbytes, SEC_DIR_DECRYPT); ++} ++ ++static struct crypto_alg ecb_aes_alg = { ++ .cra_name = "ecb(aes)", ++ .cra_driver_name = "ecb-aes-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_COMPOSITE_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_aes_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(ecb_aes_alg.cra_list), ++ .cra_u = { ++ .blkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .setkey = aes_set_key, ++ .encrypt = ecb_aes_encrypt, ++ .decrypt = ecb_aes_decrypt, ++ } ++ } ++}; ++ ++#if CRYPTO_UBICOM32_LOOP_ASM ++void __ocm_text cbc_aes_encrypt_loop(u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ asm volatile ( ++ "; set init. iv 4w \n\t" ++ " move.4 0x50(%0), 0x0(%3) \n\t" ++ " move.4 0x54(%0), 0x4(%3) \n\t" ++ " move.4 0x58(%0), 0x8(%3) \n\t" ++ " move.4 0x5c(%0), 0xc(%3) \n\t" ++ " \n\t" ++ "; we know n > 0, so we can always \n\t" ++ "; load the first block \n\t" ++ "; set input 4w \n\t" ++ " move.4 0x30(%0), 0x0(%2) \n\t" ++ " move.4 0x34(%0), 0x4(%2) \n\t" ++ " move.4 0x38(%0), 0x8(%2) \n\t" ++ " move.4 0x3c(%0), 0xc(%2) \n\t" ++ " \n\t" ++ "; kickoff hw \n\t" ++ " move.4 0x40(%0), %2 \n\t" ++ " \n\t" ++ "; update n & flush \n\t" ++ " add.4 %4, #-16, %4 \n\t" ++ " pipe_flush 0 \n\t" ++ " \n\t" ++ "; while (n): work on 2nd block \n\t" ++ " 1: lsl.4 d15, %4, #0x0 \n\t" ++ " jmpeq.f 5f \n\t" ++ " \n\t" ++ "; set input 4w (2nd) \n\t" ++ " move.4 0x30(%0), 0x10(%2) \n\t" ++ " move.4 0x34(%0), 0x14(%2) \n\t" ++ " move.4 0x38(%0), 0x18(%2) \n\t" ++ " move.4 0x3c(%0), 0x1c(%2) \n\t" ++ " \n\t" ++ "; update n/in asap while waiting \n\t" ++ " add.4 %4, #-16, %4 \n\t" ++ " move.4 d15, 16(%2)++ \n\t" ++ " \n\t" ++ "; wait for the previous output \n\t" ++ " btst 0x04(%0), #0 \n\t" ++ " jmpne.f -4 \n\t" ++ " \n\t" ++ "; read previous output \n\t" ++ " move.4 0x0(%1), 0x50(%0) \n\t" ++ " move.4 0x4(%1), 0x54(%0) \n\t" ++ " move.4 0x8(%1), 0x58(%0) \n\t" ++ " move.4 0xc(%1), 0x5c(%0) \n\t" ++ " \n\t" ++ "; kick off hw for 2nd input \n\t" ++ " move.4 0x40(%0), %2 \n\t" ++ " \n\t" ++ "; update out asap \n\t" ++ " move.4 d15, 16(%1)++ \n\t" ++ " \n\t" ++ "; go back to loop \n\t" ++ " jmpt 1b \n\t" ++ " \n\t" ++ "; wait for last output \n\t" ++ " 5: btst 0x04(%0), #0 \n\t" ++ " jmpne.f -4 \n\t" ++ " \n\t" ++ "; read last output \n\t" ++ " move.4 0x0(%1), 0x50(%0) \n\t" ++ " move.4 0x4(%1), 0x54(%0) \n\t" ++ " move.4 0x8(%1), 0x58(%0) \n\t" ++ " move.4 0xc(%1), 0x5c(%0) \n\t" ++ " \n\t" ++ "; copy out iv \n\t" ++ " move.4 0x0(%3), 0x50(%0) \n\t" ++ " move.4 0x4(%3), 0x54(%0) \n\t" ++ " move.4 0x8(%3), 0x58(%0) \n\t" ++ " move.4 0xc(%3), 0x5c(%0) \n\t" ++ " \n\t" ++ : ++ : "a" (SEC_BASE), "a" (out), "a" (in), "a" (iv), "d" (n) ++ : "d15", "cc" ++ ); ++} ++ ++#else ++ ++static void __ocm_text cbc_aes_encrypt_loop(u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ aes_hw_set_iv(iv); ++ while (likely(n)) { ++ aes_hw_cipher(out, in); ++ out += AES_BLOCK_SIZE; ++ in += AES_BLOCK_SIZE; ++ n -= AES_BLOCK_SIZE; ++ } ++ SEC_COPY_4W(iv, out - AES_BLOCK_SIZE); ++} ++ ++#endif ++ ++static void __ocm_text cbc_aes_decrypt_loop(u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ while (likely(n)) { ++ aes_hw_set_iv(iv); ++ SEC_COPY_4W(iv, in); ++ aes_hw_cipher(out, in); ++ out += AES_BLOCK_SIZE; ++ in += AES_BLOCK_SIZE; ++ n -= AES_BLOCK_SIZE; ++ } ++} ++ ++static int __ocm_text cbc_aes_crypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes, u32 extra_flags) ++{ ++ struct ubicom32_aes_ctx *uctx = crypto_blkcipher_ctx(desc->tfm); ++ int ret; ++ ++ struct blkcipher_walk walk; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ ret = blkcipher_walk_virt(desc, &walk); ++ if (unlikely(ret)) { ++ return ret; ++ } ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ ++ hw_crypto_set_ctrl(uctx->ctrl | extra_flags); ++ aes_hw_set_key(uctx->key, uctx->key_len); ++ ++ while (likely((nbytes = walk.nbytes))) { ++ /* only use complete blocks */ ++ unsigned int n = nbytes & ~(AES_BLOCK_SIZE - 1); ++ if (likely(n)) { ++ u8 *out = walk.dst.virt.addr; ++ u8 *in = walk.src.virt.addr; ++ ++ if (extra_flags & SEC_DIR_ENCRYPT) { ++ cbc_aes_encrypt_loop(out, in, walk.iv, n); ++ } else { ++ cbc_aes_decrypt_loop(out, in, walk.iv, n); ++ } ++ } ++ ++ nbytes &= AES_BLOCK_SIZE - 1; ++ ret = blkcipher_walk_done(desc, &walk, nbytes); ++ } ++ hw_crypto_unlock(); ++ ++ return ret; ++} ++ ++static int __ocm_text cbc_aes_encrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes) ++{ ++ return cbc_aes_crypt(desc, dst, src, nbytes, SEC_DIR_ENCRYPT | SEC_CBC_SET); ++} ++ ++static int __ocm_text cbc_aes_decrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes) ++{ ++ return cbc_aes_crypt(desc, dst, src, nbytes, SEC_DIR_DECRYPT | SEC_CBC_SET); ++} ++ ++static struct crypto_alg cbc_aes_alg = { ++ .cra_name = "cbc(aes)", ++ .cra_driver_name = "cbc-aes-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_COMPOSITE_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_aes_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(cbc_aes_alg.cra_list), ++ .cra_u = { ++ .blkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = aes_set_key, ++ .encrypt = cbc_aes_encrypt, ++ .decrypt = cbc_aes_decrypt, ++ } ++ } ++}; ++ ++static int __init aes_init(void) ++{ ++ int ret; ++ ++ hw_crypto_init(); ++ ++ ret = crypto_register_alg(&aes_alg); ++ if (ret) ++ goto aes_err; ++ ++ ret = crypto_register_alg(&ecb_aes_alg); ++ if (ret) ++ goto ecb_aes_err; ++ ++ ret = crypto_register_alg(&cbc_aes_alg); ++ if (ret) ++ goto cbc_aes_err; ++ ++out: ++ return ret; ++ ++cbc_aes_err: ++ crypto_unregister_alg(&ecb_aes_alg); ++ecb_aes_err: ++ crypto_unregister_alg(&aes_alg); ++aes_err: ++ goto out; ++} ++ ++static void __exit aes_fini(void) ++{ ++ crypto_unregister_alg(&cbc_aes_alg); ++ crypto_unregister_alg(&ecb_aes_alg); ++ crypto_unregister_alg(&aes_alg); ++} ++ ++module_init(aes_init); ++module_exit(aes_fini); ++ ++MODULE_ALIAS("aes"); ++ ++MODULE_DESCRIPTION("Rijndael (AES) Cipher Algorithm"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/ubicom32/crypto/crypto_des.h +@@ -0,0 +1,34 @@ ++/* ++ * arch/ubicom32/crypto/crypto_des.h ++ * Function for checking keys for the DES and Triple DES Encryption ++ * algorithms. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef __CRYPTO_DES_H__ ++#define __CRYPTO_DES_H__ ++ ++extern int crypto_des_check_key(const u8*, unsigned int, u32*); ++ ++#endif /* __CRYPTO_DES_H__ */ +--- /dev/null ++++ b/arch/ubicom32/crypto/crypto_ubicom32.c +@@ -0,0 +1,50 @@ ++/* ++ * arch/ubicom32/crypto/crypto_ubicom32.c ++ * Generic code to support ubicom32 hardware crypto accelerator ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include "crypto_ubicom32.h" ++ ++spinlock_t crypto_ubicom32_lock; ++bool crypto_ubicom32_inited = false; ++volatile bool crypto_ubicom32_on = false; ++volatile unsigned long crypto_ubicom32_last_use; ++ ++struct timer_list crypto_ubicom32_ps_timer; ++void crypto_ubicom32_ps_check(unsigned long data) ++{ ++ unsigned long idle_time = msecs_to_jiffies(HW_CRYPTO_PS_MAX_IDLE_MS); ++ ++ BUG_ON(!crypto_ubicom32_on); ++ ++ if (((jiffies - crypto_ubicom32_last_use) > idle_time) && spin_trylock_bh(&crypto_ubicom32_lock)) { ++ hw_crypto_turn_off(); ++ spin_unlock_bh(&crypto_ubicom32_lock); ++ return; ++ } ++ ++ /* keep monitoring */ ++ hw_crypto_ps_start(); ++} +--- /dev/null ++++ b/arch/ubicom32/crypto/crypto_ubicom32.h +@@ -0,0 +1,346 @@ ++/* ++ * arch/ubicom32/crypto/crypto_ubicom32.h ++ * Support for Ubicom32 cryptographic instructions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _CRYPTO_ARCH_UBICOM32_CRYPT_H ++#define _CRYPTO_ARCH_UBICOM32_CRYPT_H ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/jiffies.h> ++#include <linux/timer.h> ++#include <linux/spinlock.h> ++#include <asm/errno.h> ++#include <asm/io.h> ++#include <asm/ip5000.h> ++ ++#define CRYPTO_UBICOM32_LOOP_ASM 1 ++#define CRYPTO_UBICOM32_ALIGNMENT 4 ++#define SEC_ALIGNED(p) (((u32)p & 3) == 0) ++ ++#define SEC_BASE SECURITY_BASE ++#define SEC_KEY_OFFSET SECURITY_KEY_VALUE(0) ++#define SEC_INPUT_OFFSET SECURITY_KEY_IN(0) ++#define SEC_OUTPUT_OFFSET SECURITY_KEY_OUT(0) ++#define SEC_HASH_OFFSET SECURITY_KEY_HASH(0) ++ ++#define SEC_KEY_128_BITS SECURITY_CTRL_KEY_SIZE(0) ++#define SEC_KEY_192_BITS SECURITY_CTRL_KEY_SIZE(1) ++#define SEC_KEY_256_BITS SECURITY_CTRL_KEY_SIZE(2) ++ ++#define SEC_HASH_NONE SECURITY_CTRL_HASH_ALG_NONE ++#define SEC_HASH_MD5 SECURITY_CTRL_HASH_ALG_MD5 ++#define SEC_HASH_SHA1 SECURITY_CTRL_HASH_ALG_SHA1 ++ ++#define SEC_CBC_SET SECURITY_CTRL_CBC ++#define SEC_CBC_NONE 0 ++ ++#define SEC_ALG_AES SECURITY_CTRL_CIPHER_ALG_AES ++#define SEC_ALG_NONE SECURITY_CTRL_CIPHER_ALG_NONE ++#define SEC_ALG_DES SECURITY_CTRL_CIPHER_ALG_DES ++#define SEC_ALG_3DES SECURITY_CTRL_CIPHER_ALG_3DES ++ ++#define SEC_DIR_ENCRYPT SECURITY_CTRL_ENCIPHER ++#define SEC_DIR_DECRYPT 0 ++ ++#define CRYPTO_UBICOM32_PRIORITY 300 ++#define CRYPTO_UBICOM32_COMPOSITE_PRIORITY 400 ++ ++#define HW_CRYPTO_PS_MAX_IDLE_MS 100 /* idle time (ms) before shuting down sm */ ++ ++extern spinlock_t crypto_ubicom32_lock; ++extern bool crypto_ubicom32_inited; ++extern volatile bool crypto_ubicom32_on; ++extern volatile unsigned long crypto_ubicom32_last_use; ++extern struct timer_list crypto_ubicom32_ps_timer; ++extern void crypto_ubicom32_ps_check(unsigned long data); ++ ++#define SEC_COPY_2W(t, s) \ ++ asm volatile ( \ ++ " move.4 0(%0), 0(%1) \n\t" \ ++ " move.4 4(%0), 4(%1) \n\t" \ ++ \ ++ : \ ++ : "a" (t), "a" (s) \ ++ ) ++ ++#define SEC_COPY_4W(t, s) \ ++ asm volatile ( \ ++ " move.4 0(%0), 0(%1) \n\t" \ ++ " move.4 4(%0), 4(%1) \n\t" \ ++ " move.4 8(%0), 8(%1) \n\t" \ ++ " move.4 12(%0), 12(%1) \n\t" \ ++ : \ ++ : "a" (t), "a" (s) \ ++ ) ++ ++#define SEC_COPY_5W(t, s) \ ++ asm volatile ( \ ++ " move.4 0(%0), 0(%1) \n\t" \ ++ " move.4 4(%0), 4(%1) \n\t" \ ++ " move.4 8(%0), 8(%1) \n\t" \ ++ " move.4 12(%0), 12(%1) \n\t" \ ++ " move.4 16(%0), 16(%1) \n\t" \ ++ : \ ++ : "a" (t), "a" (s) \ ++ ) ++ ++#define SEC_SET_KEY_2W(x) \ ++ asm volatile ( \ ++ " ; write key to Security Keyblock \n\t" \ ++ " move.4 0x10(%0), 0(%1) \n\t" \ ++ " move.4 0x14(%0), 4(%1) \n\t" \ ++ : \ ++ : "a" (SECURITY_BASE), "a" (x) \ ++ ) ++ ++#define SEC_SET_KEY_4W(x) \ ++ asm volatile ( \ ++ " ; write key to Security Keyblock \n\t" \ ++ " move.4 0x10(%0), 0(%1) \n\t" \ ++ " move.4 0x14(%0), 4(%1) \n\t" \ ++ " move.4 0x18(%0), 8(%1) \n\t" \ ++ " move.4 0x1c(%0), 12(%1) \n\t" \ ++ : \ ++ : "a"(SECURITY_BASE), "a"(x) \ ++ ) ++ ++#define SEC_SET_KEY_6W(x) \ ++ asm volatile ( \ ++ " ; write key to Security Keyblock \n\t" \ ++ " move.4 0x10(%0), 0(%1) \n\t" \ ++ " move.4 0x14(%0), 4(%1) \n\t" \ ++ " move.4 0x18(%0), 8(%1) \n\t" \ ++ " move.4 0x1c(%0), 12(%1) \n\t" \ ++ " move.4 0x20(%0), 16(%1) \n\t" \ ++ " move.4 0x24(%0), 20(%1) \n\t" \ ++ : \ ++ : "a" (SECURITY_BASE), "a" (x) \ ++ ) ++ ++#define SEC_SET_KEY_8W(x) \ ++ asm volatile ( \ ++ " ; write key to Security Keyblock \n\t" \ ++ " move.4 0x10(%0), 0(%1) \n\t" \ ++ " move.4 0x14(%0), 4(%1) \n\t" \ ++ " move.4 0x18(%0), 8(%1) \n\t" \ ++ " move.4 0x1c(%0), 12(%1) \n\t" \ ++ " move.4 0x20(%0), 16(%1) \n\t" \ ++ " move.4 0x24(%0), 20(%1) \n\t" \ ++ " move.4 0x28(%0), 24(%1) \n\t" \ ++ " move.4 0x2c(%0), 28(%1) \n\t" \ ++ : \ ++ : "a" (SECURITY_BASE), "a" (x) \ ++ ) ++ ++#define SEC_SET_KEY_64(k) SEC_SET_KEY_2W(k) ++#define SEC_SET_KEY_128(k) SEC_SET_KEY_4W(k) ++#define SEC_SET_KEY_192(k) SEC_SET_KEY_6W(k) ++#define SEC_SET_KEY_256(k) SEC_SET_KEY_8W(k) ++ ++#define DES_SET_KEY(x) SEC_SET_KEY_64(x) ++#define DES3_SET_KEY(x) SEC_SET_KEY_192(x) ++ ++#define SEC_SET_INPUT_2W(x) \ ++ asm volatile ( \ ++ " ; write key to Security Keyblock \n\t" \ ++ " move.4 0x30(%0), 0(%1) \n\t" \ ++ " move.4 0x34(%0), 4(%1) \n\t" \ ++ : \ ++ : "a" (SECURITY_BASE), "a" (x) \ ++ ) ++ ++#define SEC_GET_OUTPUT_2W(x) \ ++ asm volatile ( \ ++ " ; write key to Security Keyblock \n\t" \ ++ " move.4 0(%1), 0x50(%0) \n\t" \ ++ " move.4 4(%1), 0x54(%0) \n\t" \ ++ : \ ++ : "a" (SECURITY_BASE), "a" (x) \ ++ ) ++ ++#define SEC_SET_INPUT_4W(x) \ ++ asm volatile ( \ ++ " ; write key to Security Keyblock \n\t" \ ++ " move.4 0x30(%0), 0(%1) \n\t" \ ++ " move.4 0x34(%0), 4(%1) \n\t" \ ++ " move.4 0x38(%0), 8(%1) \n\t" \ ++ " move.4 0x3c(%0), 12(%1) \n\t" \ ++ : \ ++ : "a" (SECURITY_BASE), "a" (x) \ ++ ) ++ ++#define SEC_GET_OUTPUT_4W(x) \ ++ asm volatile ( \ ++ " ; read output from Security Keyblock \n\t" \ ++ " move.4 0(%1), 0x50(%0) \n\t" \ ++ " move.4 4(%1), 0x54(%0) \n\t" \ ++ " move.4 8(%1), 0x58(%0) \n\t" \ ++ " move.4 12(%1), 0x5c(%0) \n\t" \ ++ : \ ++ : "a" (SECURITY_BASE), "a" (x) \ ++ ) ++ ++#define SEC_SET_IV_4W(x) \ ++ asm volatile ( \ ++ " ; write IV to Security Keyblock \n\t" \ ++ " move.4 0x50(%0), 0(%1) \n\t" \ ++ " move.4 0x54(%0), 4(%1) \n\t" \ ++ " move.4 0x58(%0), 8(%1) \n\t" \ ++ " move.4 0x5c(%0), 12(%1) \n\t" \ ++ : \ ++ : "a" (SECURITY_BASE), "a" (x) \ ++ ) ++ ++#define SEC_PIPE_FLUSH() asm volatile ( " pipe_flush 0 \n\t" ) ++ ++static inline void hw_crypto_set_ctrl(uint32_t c) ++{ ++ asm volatile ( ++ " move.4 0(%0), %1 \n\t" ++ : ++ : "a" (SECURITY_BASE + SECURITY_CTRL), "d" (c) ++ ); ++} ++ ++static inline void hw_crypto_ps_start(void) ++{ ++ crypto_ubicom32_ps_timer.expires = jiffies + msecs_to_jiffies(HW_CRYPTO_PS_MAX_IDLE_MS >> 1); ++ add_timer(&crypto_ubicom32_ps_timer); ++} ++ ++static inline void hw_crypto_turn_on(void) ++{ ++ asm volatile ( ++ " moveai A4, %0 \n\t" ++ " bset 0x0(A4), 0x0(A4), %1 \n\t" ++ " cycles 11 \n\t" ++ : ++ : "i" (OCP_BASE >> 7), "i" (GEN_CLK_PLL_SECURITY_BIT_NO) ++ : "a4", "cc" ++ ); ++ crypto_ubicom32_on = true; ++} ++ ++static inline void hw_crypto_turn_off(void) ++{ ++ asm volatile ( ++ " moveai A4, %0 \n\t" ++ " bclr 0x0(A4), 0x0(A4), %1 \n\t" ++ : ++ : "i" (OCP_BASE >> 7), "i" (GEN_CLK_PLL_SECURITY_BIT_NO) ++ : "a4", "cc" ++ ); ++ crypto_ubicom32_on = false; ++} ++ ++/* ++ * hw_crypto_check ++ * Most probably hw crypto is called in clusters and it makes no sense to turn it off ++ * and on and waster 13 cycles every time. ++ */ ++static inline void hw_crypto_check(void) ++{ ++ if (likely(crypto_ubicom32_on)) { ++ return; ++ } ++ crypto_ubicom32_last_use = jiffies; ++ hw_crypto_turn_on(); ++ hw_crypto_ps_start(); ++} ++ ++/* ++ * hw_crypto_ps_init ++ * Init power save timer ++ */ ++static inline void hw_crypto_ps_init(void) ++{ ++ init_timer_deferrable(&crypto_ubicom32_ps_timer); ++ crypto_ubicom32_ps_timer.function = crypto_ubicom32_ps_check; ++ crypto_ubicom32_ps_timer.data = 0; ++} ++ ++/* ++ * hw_crypto_init() ++ * Initialize OCP security module lock and disables its clock. ++ */ ++static inline void hw_crypto_init(void) ++{ ++ if (!crypto_ubicom32_inited) { ++ crypto_ubicom32_inited = true; ++ spin_lock_init(&crypto_ubicom32_lock); ++ hw_crypto_ps_init(); ++ hw_crypto_turn_off(); ++ } ++} ++ ++/* ++ * hw_crypto_lock() ++ * Locks the OCP security module and enables its clock. ++ */ ++static inline void hw_crypto_lock(void) ++{ ++ spin_lock_bh(&crypto_ubicom32_lock); ++} ++ ++/* ++ * hw_crypto_unlock() ++ * Unlocks the OCP security module and disables its clock. ++ */ ++static inline void hw_crypto_unlock(void) ++{ ++ crypto_ubicom32_last_use = jiffies; ++ spin_unlock_bh(&crypto_ubicom32_lock); ++} ++ ++#define CONFIG_CRYPTO_UBICOM32_DEBUG 1 ++ ++#ifdef CONFIG_CRYPTO_UBICOM32_DEBUG ++static inline void hex_dump(void *buf, int b_size, const char *msg) ++{ ++ u8 *b = (u8 *)buf; ++ int i; ++ if (msg) { ++ printk("%s:\t", msg); ++ } ++ ++ for (i=0; i < b_size; i++) { ++ printk("%02x ", b[i]); ++ if ((i & 3) == 3) { ++ printk(" "); ++ } ++ if ((i & 31) == 31) { ++ printk("\n"); ++ } ++ } ++ printk("\n"); ++} ++#define UBICOM32_SEC_DUMP(a, b, c) hex_dump(a, b, c) ++#else ++#define UBICOM32_SEC_DUMP(a, b, c) ++#endif ++ ++#endif /* _CRYPTO_ARCH_UBICOM32_CRYPT_H */ +--- /dev/null ++++ b/arch/ubicom32/crypto/des_check_key.c +@@ -0,0 +1,148 @@ ++/* ++ * arch/ubicom32/crypto/des_check_key.c ++ * Ubicom32 architecture function for checking keys for the DES and ++ * Tripple DES Encryption algorithms. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * Originally released as descore by Dana L. How <how@isl.stanford.edu>. ++ * Modified by Raimar Falke <rf13@inf.tu-dresden.de> for the Linux-Kernel. ++ * Derived from Cryptoapi and Nettle implementations, adapted for in-place ++ * scatterlist interface. Changed LGPL to GPL per section 3 of the LGPL. ++ * ++ * s390 Version: ++ * Copyright IBM Corp. 2003 ++ * Author(s): Thomas Spatzier ++ * Jan Glauber (jan.glauber@de.ibm.com) ++ * ++ * Derived from "crypto/des.c" ++ * Copyright (c) 1992 Dana L. How. ++ * Copyright (c) Raimar Falke <rf13@inf.tu-dresden.de> ++ * Copyright (c) Gisle Sflensminde <gisle@ii.uib.no> ++ * Copyright (C) 2001 Niels Mvller. ++ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/crypto.h> ++#include "crypto_des.h" ++ ++#define ROR(d,c,o) ((d) = (d) >> (c) | (d) << (o)) ++ ++static const u8 parity[] = { ++ 8,1,0,8,0,8,8,0,0,8,8,0,8,0,2,8,0,8,8,0,8,0,0,8,8,0,0,8,0,8,8,3, ++ 0,8,8,0,8,0,0,8,8,0,0,8,0,8,8,0,8,0,0,8,0,8,8,0,0,8,8,0,8,0,0,8, ++ 0,8,8,0,8,0,0,8,8,0,0,8,0,8,8,0,8,0,0,8,0,8,8,0,0,8,8,0,8,0,0,8, ++ 8,0,0,8,0,8,8,0,0,8,8,0,8,0,0,8,0,8,8,0,8,0,0,8,8,0,0,8,0,8,8,0, ++ 0,8,8,0,8,0,0,8,8,0,0,8,0,8,8,0,8,0,0,8,0,8,8,0,0,8,8,0,8,0,0,8, ++ 8,0,0,8,0,8,8,0,0,8,8,0,8,0,0,8,0,8,8,0,8,0,0,8,8,0,0,8,0,8,8,0, ++ 8,0,0,8,0,8,8,0,0,8,8,0,8,0,0,8,0,8,8,0,8,0,0,8,8,0,0,8,0,8,8,0, ++ 4,8,8,0,8,0,0,8,8,0,0,8,0,8,8,0,8,5,0,8,0,8,8,0,0,8,8,0,8,0,6,8, ++}; ++ ++/* ++ * RFC2451: Weak key checks SHOULD be performed. ++ */ ++int ++crypto_des_check_key(const u8 *key, unsigned int keylen, u32 *flags) ++{ ++ u32 n, w; ++ ++ n = parity[key[0]]; n <<= 4; ++ n |= parity[key[1]]; n <<= 4; ++ n |= parity[key[2]]; n <<= 4; ++ n |= parity[key[3]]; n <<= 4; ++ n |= parity[key[4]]; n <<= 4; ++ n |= parity[key[5]]; n <<= 4; ++ n |= parity[key[6]]; n <<= 4; ++ n |= parity[key[7]]; ++ w = 0x88888888L; ++ ++ if ((*flags & CRYPTO_TFM_REQ_WEAK_KEY) ++ && !((n - (w >> 3)) & w)) { /* 1 in 10^10 keys passes this test */ ++ if (n < 0x41415151) { ++ if (n < 0x31312121) { ++ if (n < 0x14141515) { ++ /* 01 01 01 01 01 01 01 01 */ ++ if (n == 0x11111111) goto weak; ++ /* 01 1F 01 1F 01 0E 01 0E */ ++ if (n == 0x13131212) goto weak; ++ } else { ++ /* 01 E0 01 E0 01 F1 01 F1 */ ++ if (n == 0x14141515) goto weak; ++ /* 01 FE 01 FE 01 FE 01 FE */ ++ if (n == 0x16161616) goto weak; ++ } ++ } else { ++ if (n < 0x34342525) { ++ /* 1F 01 1F 01 0E 01 0E 01 */ ++ if (n == 0x31312121) goto weak; ++ /* 1F 1F 1F 1F 0E 0E 0E 0E (?) */ ++ if (n == 0x33332222) goto weak; ++ } else { ++ /* 1F E0 1F E0 0E F1 0E F1 */ ++ if (n == 0x34342525) goto weak; ++ /* 1F FE 1F FE 0E FE 0E FE */ ++ if (n == 0x36362626) goto weak; ++ } ++ } ++ } else { ++ if (n < 0x61616161) { ++ if (n < 0x44445555) { ++ /* E0 01 E0 01 F1 01 F1 01 */ ++ if (n == 0x41415151) goto weak; ++ /* E0 1F E0 1F F1 0E F1 0E */ ++ if (n == 0x43435252) goto weak; ++ } else { ++ /* E0 E0 E0 E0 F1 F1 F1 F1 (?) */ ++ if (n == 0x44445555) goto weak; ++ /* E0 FE E0 FE F1 FE F1 FE */ ++ if (n == 0x46465656) goto weak; ++ } ++ } else { ++ if (n < 0x64646565) { ++ /* FE 01 FE 01 FE 01 FE 01 */ ++ if (n == 0x61616161) goto weak; ++ /* FE 1F FE 1F FE 0E FE 0E */ ++ if (n == 0x63636262) goto weak; ++ } else { ++ /* FE E0 FE E0 FE F1 FE F1 */ ++ if (n == 0x64646565) goto weak; ++ /* FE FE FE FE FE FE FE FE */ ++ if (n == 0x66666666) goto weak; ++ } ++ } ++ } ++ } ++ return 0; ++weak: ++ *flags |= CRYPTO_TFM_RES_WEAK_KEY; ++ return -EINVAL; ++} ++ ++EXPORT_SYMBOL(crypto_des_check_key); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Key Check function for DES & DES3 Cipher Algorithms"); +--- /dev/null ++++ b/arch/ubicom32/crypto/des_ubicom32.c +@@ -0,0 +1,761 @@ ++/* ++ * arch/ubicom32/crypto/des_ubicom32.c ++ * Ubicom32 implementation of the DES Cipher Algorithm. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <crypto/algapi.h> ++#include <linux/init.h> ++#include <linux/module.h> ++ ++#include "crypto_ubicom32.h" ++extern int crypto_des_check_key(const u8 *key, unsigned int keylen, u32 *flags); ++ ++#define DES_BLOCK_SIZE 8 ++#define DES_KEY_SIZE 8 ++ ++#define DES3_192_KEY_SIZE (3 * DES_KEY_SIZE) ++#define DES3_192_BLOCK_SIZE DES_BLOCK_SIZE ++ ++#define DES3_SUB_KEY(key, i) (((u8 *)key) + (i * DES_KEY_SIZE)) ++ ++enum des_ops { ++ DES_ENCRYPT, ++ DES_DECRYPT, ++ ++ DES3_EDE_ENCRYPT, ++ DES3_EDE_DECRYPT, ++ ++#ifdef DES3_EEE ++ DES3_EEE_ENCRYPT, ++ DES3_EEE_DECRYPT, ++#endif ++}; ++ ++struct ubicom32_des_ctx { ++ u8 key[3 * DES_KEY_SIZE]; ++ u32 ctrl; ++ int key_len; ++}; ++ ++static inline void des_hw_set_key(const u8 *key, u8 key_len) ++{ ++ /* ++ * HW 3DES is not tested yet, use DES just as ipOS ++ */ ++ DES_SET_KEY(key); ++} ++ ++static inline void des_hw_cipher(u8 *out, const u8 *in) ++{ ++ SEC_SET_INPUT_2W(in); ++ ++ asm volatile ( ++ " ; start DES by writing 0x38(SECURITY_BASE) \n\t" ++ " move.4 0x38(%0), #0x01 \n\t" ++ " pipe_flush 0 \n\t" ++ " \n\t" ++ " ; wait for the module to calculate the output \n\t" ++ " btst 0x04(%0), #0 \n\t" ++ " jmpne.f .-4 \n\t" ++ : ++ : "a" (SEC_BASE) ++ : "cc" ++ ); ++ ++ SEC_GET_OUTPUT_2W(out); ++} ++ ++ ++static void inline des3_hw_ede_encrypt(u8 *keys, u8 *out, const u8 *in) ++{ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_ENCRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 0), DES_KEY_SIZE); ++ des_hw_cipher(out, in); ++ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_DECRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 1), DES_KEY_SIZE); ++ des_hw_cipher(out, out); ++ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_ENCRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 2), DES_KEY_SIZE); ++ des_hw_cipher(out, out); ++} ++ ++static void inline des3_hw_ede_decrypt(u8 *keys, u8 *out, const u8 *in) ++{ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_DECRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 2), DES_KEY_SIZE); ++ des_hw_cipher(out, in); ++ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_ENCRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 1), DES_KEY_SIZE); ++ des_hw_cipher(out, out); ++ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_DECRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 0), DES_KEY_SIZE); ++ des_hw_cipher(out, out); ++} ++ ++#ifdef DES3_EEE ++static void inline des3_hw_eee_encrypt(u8 *keys, u8 *out, const u8 *in) ++{ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_ENCRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 0), 2); ++ des_hw_cipher(out, in); ++ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_ENCRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 1), 2); ++ des_hw_cipher(out, out); ++ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_ENCRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 2), 2); ++ des_hw_cipher(out, out); ++} ++ ++static void inline des3_hw_eee_decrypt(u8 *keys, u8 *out, const u8 *in) ++{ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_DECRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 2), 2); ++ des_hw_cipher(out, in); ++ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_DECRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 1), 2); ++ des_hw_cipher(out, out); ++ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_DECRYPT); ++ des_hw_set_key(DES3_SUB_KEY(keys, 0), 2); ++ des_hw_cipher(out, out); ++} ++#endif ++ ++static int des_setkey(struct crypto_tfm *tfm, const u8 *key, ++ unsigned int keylen) ++{ ++ struct ubicom32_des_ctx *dctx = crypto_tfm_ctx(tfm); ++ u32 *flags = &tfm->crt_flags; ++ int ret; ++ ++ /* test if key is valid (not a weak key) */ ++ ret = crypto_des_check_key(key, keylen, flags); ++ if (ret == 0) { ++ memcpy(dctx->key, key, keylen); ++ dctx->key_len = keylen; ++ //dctx->ctrl = (keylen == DES_KEY_SIZE) ? SEC_ALG_DES : SEC_ALG_3DES ++ /* 2DES and 3DES are both implemented with DES hw function */ ++ dctx->ctrl = SEC_ALG_DES; ++ } ++ return ret; ++} ++ ++static inline void des_cipher_1b(struct crypto_tfm *tfm, u8 *out, const u8 *in, u32 extra_flags) ++{ ++ const struct ubicom32_des_ctx *uctx = crypto_tfm_ctx(tfm); ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ hw_crypto_set_ctrl(uctx->ctrl | extra_flags); ++ ++ des_hw_set_key(uctx->key, uctx->key_len); ++ des_hw_cipher(out, in); ++ ++ hw_crypto_unlock(); ++} ++ ++static void des_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) ++{ ++ des_cipher_1b(tfm, out, in, SEC_DIR_ENCRYPT); ++} ++ ++static void des_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) ++{ ++ des_cipher_1b(tfm, out, in, SEC_DIR_DECRYPT); ++} ++ ++static struct crypto_alg des_alg = { ++ .cra_name = "des", ++ .cra_driver_name = "des-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_CIPHER, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_des_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(des_alg.cra_list), ++ .cra_u = { ++ .cipher = { ++ .cia_min_keysize = DES_KEY_SIZE, ++ .cia_max_keysize = DES_KEY_SIZE, ++ .cia_setkey = des_setkey, ++ .cia_encrypt = des_encrypt, ++ .cia_decrypt = des_decrypt, ++ } ++ } ++}; ++ ++static void ecb_des_ciper_loop(u8 *out, u8 *in, unsigned int n) ++{ ++ while (likely(n)) { ++ des_hw_cipher(out, in); ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++static void ecb_des3_ede_encrypt_loop(u8 *keys, u8 *out, u8 *in, unsigned int n) ++{ ++ while (likely(n)) { ++ des3_hw_ede_encrypt(keys, out, in); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++static void ecb_des3_ede_decrypt_loop(u8 *keys, u8 *out, u8 *in, unsigned int n) ++{ ++ while (likely(n)) { ++ des3_hw_ede_decrypt(keys, out, in); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++#ifdef DES3_EEE ++static void ecb_des3_eee_encrypt_loop(u8 *keys, u8 *out, u8 *in, unsigned int n) ++{ ++ while (likely(n)) { ++ des3_hw_eee_encrypt(keys, out, in); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++static void ecb_des3_eee_decrypt_loop(u8 *keys, u8 *out, u8 *in, unsigned int n) ++{ ++ while (likely(n)) { ++ des3_hw_eee_decrypt(keys, out, in); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++#endif ++ ++static inline void ecb_des_cipher_n(struct ubicom32_des_ctx *uctx, enum des_ops op, u8 *out, u8 *in, unsigned int n) ++{ ++ switch (op) { ++ case DES_ENCRYPT: ++ case DES_DECRYPT: ++ /* set the right algo, direction and key once */ ++ hw_crypto_set_ctrl(SEC_ALG_DES | (op == DES_ENCRYPT ? SEC_DIR_ENCRYPT : 0)); ++ des_hw_set_key(uctx->key, uctx->key_len); ++ ecb_des_ciper_loop(out, in, n); ++ break; ++ ++ case DES3_EDE_ENCRYPT: ++ ecb_des3_ede_encrypt_loop(uctx->key, out, in, n); ++ break; ++ ++ case DES3_EDE_DECRYPT: ++ ecb_des3_ede_decrypt_loop(uctx->key, out, in, n); ++ break; ++ ++#ifdef DES3_EEE ++ case DES3_EEE_ENCRYPT: ++ ecb_des3_eee_encrypt_loop(uctx->key, out, in, n); ++ break; ++ ++ case DES3_EEE_DECRYPT: ++ ecb_des3_eee_decrypt_loop(uctx->key, out, in, n); ++ break; ++#endif ++ } ++} ++ ++static inline void des_xor_2w(u32 *data, u32 *iv) ++{ ++ data[0] ^= iv[0]; ++ data[1] ^= iv[1]; ++} ++ ++static void cbc_des_encrypt_loop(u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ while (likely(n)) { ++ des_xor_2w((u32 *)in, (u32 *)iv); ++ des_hw_cipher(out, in); ++ SEC_COPY_2W(iv, out); ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++static void cbc_des_decrypt_loop(u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ u8 next_iv[DES_BLOCK_SIZE]; ++ while (likely(n)) { ++ SEC_COPY_2W(next_iv, in); ++ des_hw_cipher(out, in); ++ des_xor_2w((u32 *)out, (u32 *)iv); ++ SEC_COPY_2W(iv, next_iv); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++static void cbc_des3_ede_encrypt_loop(u8 *keys, u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ while (likely(n)) { ++ des_xor_2w((u32 *)in, (u32 *)iv); ++ des3_hw_ede_encrypt(keys, out, in); ++ SEC_COPY_2W(iv, out); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++static void cbc_des3_ede_decrypt_loop(u8 *keys, u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ u8 next_iv[DES_BLOCK_SIZE]; ++ while (likely(n)) { ++ SEC_COPY_2W(next_iv, in); ++ des3_hw_ede_decrypt(keys, out, in); ++ des_xor_2w((u32 *)out, (u32 *)iv); ++ SEC_COPY_2W(iv, next_iv); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++#ifdef DES3_EEE ++static void cbc_des3_eee_encrypt_loop(u8 *keys, u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ while (likely(n)) { ++ des_xor_2w((u32 *)in, (u32 *)iv); ++ des3_hw_eee_encrypt(keys, out, in); ++ SEC_COPY_2W(iv, out); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++ ++static void cbc_des3_eee_decrypt_loop(u8 *keys, u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ u8 next_iv[DES_BLOCK_SIZE]; ++ while (likely(n)) { ++ SEC_COPY_2W(next_iv, in); ++ des3_hw_eee_decrypt(keys, out, in); ++ des_xor_2w((u32 *)out, (u32 *)iv); ++ SEC_COPY_2W(iv, next_iv); ++ ++ out += DES_BLOCK_SIZE; ++ in += DES_BLOCK_SIZE; ++ n -= DES_BLOCK_SIZE; ++ } ++} ++#endif ++ ++static inline void cbc_des_cipher_n(struct ubicom32_des_ctx *uctx, enum des_ops op, u8 *out, u8 *in, u8 *iv, unsigned int n) ++{ ++ switch (op) { ++ case DES_ENCRYPT: ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_ENCRYPT); ++ des_hw_set_key(uctx->key, uctx->key_len); ++ cbc_des_encrypt_loop(out, in, iv, n); ++ break; ++ ++ case DES_DECRYPT: ++ /* set the right algo, direction and key once */ ++ hw_crypto_set_ctrl(SEC_ALG_DES | SEC_DIR_DECRYPT); ++ des_hw_set_key(uctx->key, uctx->key_len); ++ cbc_des_decrypt_loop(out, in, iv, n); ++ break; ++ ++ case DES3_EDE_ENCRYPT: ++ cbc_des3_ede_encrypt_loop(uctx->key, out, in, iv, n); ++ break; ++ ++ case DES3_EDE_DECRYPT: ++ cbc_des3_ede_decrypt_loop(uctx->key, out, in, iv, n); ++ break; ++ ++#ifdef DES3_EEE ++ case DES3_EEE_ENCRYPT: ++ cbc_des3_eee_encrypt_loop(uctx->key, out, in, iv, n); ++ break; ++ ++ case DES3_EEE_DECRYPT: ++ cbc_des3_eee_decrypt_loop(uctx->key, out, in, iv, n); ++ break; ++#endif ++ } ++} ++ ++static int des_cipher(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes, u32 extra_flags, enum des_ops op) ++{ ++ struct ubicom32_des_ctx *uctx = crypto_blkcipher_ctx(desc->tfm); ++ int ret; ++ ++ struct blkcipher_walk walk; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ ret = blkcipher_walk_virt(desc, &walk); ++ if (ret) { ++ return ret; ++ } ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ ++ while ((nbytes = walk.nbytes)) { ++ /* only use complete blocks */ ++ unsigned int n = nbytes & ~(DES_BLOCK_SIZE - 1); ++ u8 *out = walk.dst.virt.addr; ++ u8 *in = walk.src.virt.addr; ++ ++ /* finish n/16 blocks */ ++ if (extra_flags & SEC_CBC_SET) { ++ cbc_des_cipher_n(uctx, op, out, in, walk.iv, n); ++ } else { ++ ecb_des_cipher_n(uctx, op, out, in, n); ++ } ++ ++ nbytes &= DES_BLOCK_SIZE - 1; ++ ret = blkcipher_walk_done(desc, &walk, nbytes); ++ } ++ ++ hw_crypto_unlock(); ++ return ret; ++} ++ ++static int ecb_des_encrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes) ++{ ++ return des_cipher(desc, dst, src, nbytes, SEC_CBC_NONE, DES_ENCRYPT); ++} ++ ++static int ecb_des_decrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes) ++{ ++ return des_cipher(desc, dst, src, nbytes, SEC_CBC_NONE, DES_DECRYPT); ++} ++ ++static struct crypto_alg ecb_des_alg = { ++ .cra_name = "ecb(des)", ++ .cra_driver_name = "ecb-des-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_COMPOSITE_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_des_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(ecb_des_alg.cra_list), ++ .cra_u = { ++ .blkcipher = { ++ .min_keysize = DES_KEY_SIZE, ++ .max_keysize = DES_KEY_SIZE, ++ .setkey = des_setkey, ++ .encrypt = ecb_des_encrypt, ++ .decrypt = ecb_des_decrypt, ++ } ++ } ++}; ++ ++static int cbc_des_encrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes) ++{ ++ return des_cipher(desc, dst, src, nbytes, SEC_CBC_SET, DES_ENCRYPT); ++} ++ ++static int cbc_des_decrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, struct scatterlist *src, ++ unsigned int nbytes) ++{ ++ return des_cipher(desc, dst, src, nbytes, SEC_CBC_SET, DES_DECRYPT); ++} ++ ++static struct crypto_alg cbc_des_alg = { ++ .cra_name = "cbc(des)", ++ .cra_driver_name = "cbc-des-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_COMPOSITE_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_des_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(cbc_des_alg.cra_list), ++ .cra_u = { ++ .blkcipher = { ++ .min_keysize = DES_KEY_SIZE, ++ .max_keysize = DES_KEY_SIZE, ++ .ivsize = DES_BLOCK_SIZE, ++ .setkey = des_setkey, ++ .encrypt = cbc_des_encrypt, ++ .decrypt = cbc_des_decrypt, ++ } ++ } ++}; ++ ++/* ++ * RFC2451: ++ * ++ * For DES-EDE3, there is no known need to reject weak or ++ * complementation keys. Any weakness is obviated by the use of ++ * multiple keys. ++ * ++ * However, if the first two or last two independent 64-bit keys are ++ * equal (k1 == k2 or k2 == k3), then the DES3 operation is simply the ++ * same as DES. Implementers MUST reject keys that exhibit this ++ * property. ++ * ++ */ ++static int des3_192_setkey(struct crypto_tfm *tfm, const u8 *key, ++ unsigned int keylen) ++{ ++ int i, ret; ++ struct ubicom32_des_ctx *dctx = crypto_tfm_ctx(tfm); ++ const u8 *temp_key = key; ++ u32 *flags = &tfm->crt_flags; ++ ++ if (!(memcmp(key, &key[DES_KEY_SIZE], DES_KEY_SIZE) && ++ memcmp(&key[DES_KEY_SIZE], &key[DES_KEY_SIZE * 2], ++ DES_KEY_SIZE))) { ++ ++ *flags |= CRYPTO_TFM_RES_BAD_KEY_SCHED; ++ return -EINVAL; ++ } ++ for (i = 0; i < 3; i++, temp_key += DES_KEY_SIZE) { ++ ret = crypto_des_check_key(temp_key, DES_KEY_SIZE, flags); ++ if (ret < 0) ++ return ret; ++ } ++ memcpy(dctx->key, key, keylen); ++ dctx->ctrl = SEC_ALG_DES; //hw 3DES not working yet ++ dctx->key_len = keylen; ++ return 0; ++} ++ ++static void des3_192_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) ++{ ++ struct ubicom32_des_ctx *uctx = crypto_tfm_ctx(tfm); ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ ++ des3_hw_ede_encrypt(uctx->key, dst, src); ++ ++ hw_crypto_unlock(); ++} ++ ++static void des3_192_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) ++{ ++ struct ubicom32_des_ctx *uctx = crypto_tfm_ctx(tfm); ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ ++ des3_hw_ede_decrypt(uctx->key, dst, src); ++ ++ hw_crypto_unlock(); ++} ++ ++static struct crypto_alg des3_192_alg = { ++ .cra_name = "des3_ede", ++ .cra_driver_name = "des3_ede-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_CIPHER, ++ .cra_blocksize = DES3_192_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_des_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(des3_192_alg.cra_list), ++ .cra_u = { ++ .cipher = { ++ .cia_min_keysize = DES3_192_KEY_SIZE, ++ .cia_max_keysize = DES3_192_KEY_SIZE, ++ .cia_setkey = des3_192_setkey, ++ .cia_encrypt = des3_192_encrypt, ++ .cia_decrypt = des3_192_decrypt, ++ } ++ } ++}; ++ ++static int ecb_des3_192_encrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ return des_cipher(desc, dst, src, nbytes, SEC_CBC_NONE, DES3_EDE_ENCRYPT); ++} ++ ++static int ecb_des3_192_decrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ return des_cipher(desc, dst, src, nbytes, SEC_CBC_NONE, DES3_EDE_DECRYPT); ++} ++ ++static struct crypto_alg ecb_des3_192_alg = { ++ .cra_name = "ecb(des3_ede)", ++ .cra_driver_name = "ecb-des3_ede-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_COMPOSITE_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = DES3_192_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_des_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT( ++ ecb_des3_192_alg.cra_list), ++ .cra_u = { ++ .blkcipher = { ++ .min_keysize = DES3_192_KEY_SIZE, ++ .max_keysize = DES3_192_KEY_SIZE, ++ .setkey = des3_192_setkey, ++ .encrypt = ecb_des3_192_encrypt, ++ .decrypt = ecb_des3_192_decrypt, ++ } ++ } ++}; ++ ++static int cbc_des3_192_encrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ return des_cipher(desc, dst, src, nbytes, SEC_CBC_SET, DES3_EDE_ENCRYPT); ++} ++ ++static int cbc_des3_192_decrypt(struct blkcipher_desc *desc, ++ struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ return des_cipher(desc, dst, src, nbytes, SEC_CBC_SET, DES3_EDE_DECRYPT); ++} ++ ++static struct crypto_alg cbc_des3_192_alg = { ++ .cra_name = "cbc(des3_ede)", ++ .cra_driver_name = "cbc-des3_ede-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_COMPOSITE_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = DES3_192_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_des_ctx), ++ .cra_alignmask = CRYPTO_UBICOM32_ALIGNMENT - 1, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT( ++ cbc_des3_192_alg.cra_list), ++ .cra_u = { ++ .blkcipher = { ++ .min_keysize = DES3_192_KEY_SIZE, ++ .max_keysize = DES3_192_KEY_SIZE, ++ .ivsize = DES3_192_BLOCK_SIZE, ++ .setkey = des3_192_setkey, ++ .encrypt = cbc_des3_192_encrypt, ++ .decrypt = cbc_des3_192_decrypt, ++ } ++ } ++}; ++ ++static int init(void) ++{ ++ int ret = 0; ++ ++ hw_crypto_init(); ++ ++ ret = crypto_register_alg(&des_alg); ++ if (ret) ++ goto des_err; ++ ret = crypto_register_alg(&ecb_des_alg); ++ if (ret) ++ goto ecb_des_err; ++ ret = crypto_register_alg(&cbc_des_alg); ++ if (ret) ++ goto cbc_des_err; ++ ++ ret = crypto_register_alg(&des3_192_alg); ++ if (ret) ++ goto des3_192_err; ++ ret = crypto_register_alg(&ecb_des3_192_alg); ++ if (ret) ++ goto ecb_des3_192_err; ++ ret = crypto_register_alg(&cbc_des3_192_alg); ++ if (ret) ++ goto cbc_des3_192_err; ++ ++out: ++ return ret; ++ ++cbc_des3_192_err: ++ crypto_unregister_alg(&ecb_des3_192_alg); ++ecb_des3_192_err: ++ crypto_unregister_alg(&des3_192_alg); ++des3_192_err: ++ crypto_unregister_alg(&cbc_des_alg); ++cbc_des_err: ++ crypto_unregister_alg(&ecb_des_alg); ++ecb_des_err: ++ crypto_unregister_alg(&des_alg); ++des_err: ++ goto out; ++} ++ ++static void __exit fini(void) ++{ ++ crypto_unregister_alg(&cbc_des3_192_alg); ++ crypto_unregister_alg(&ecb_des3_192_alg); ++ crypto_unregister_alg(&des3_192_alg); ++ crypto_unregister_alg(&cbc_des_alg); ++ crypto_unregister_alg(&ecb_des_alg); ++ crypto_unregister_alg(&des_alg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_ALIAS("des"); ++MODULE_ALIAS("des3_ede"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("DES & Triple DES EDE Cipher Algorithms"); +--- /dev/null ++++ b/arch/ubicom32/crypto/Makefile +@@ -0,0 +1,36 @@ ++# ++# arch/ubicom32/crypto/Makefile ++# <TODO: Replace with short file description> ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++obj-$(CONFIG_CRYPTO_UBICOM32) += crypto_ubicom32.o ++obj-$(CONFIG_CRYPTO_AES_UBICOM32) += aes_ubicom32.o ++obj-$(CONFIG_CRYPTO_DES_UBICOM32) += des.o ++obj-$(CONFIG_CRYPTO_MD5_UBICOM32) += md5.o ++obj-$(CONFIG_CRYPTO_SHA1_UBICOM32) += sha1.o ++ ++des-y := des_ubicom32.o des_check_key.o ++md5-y := md5_ubicom32.o md5_ubicom32_asm.o ++sha1-y := sha1_ubicom32.o +--- /dev/null ++++ b/arch/ubicom32/crypto/md5_ubicom32_asm.S +@@ -0,0 +1,235 @@ ++/* ++ * arch/ubicom32/crypto/md5_ubicom32_asm.S ++ * MD5 (Message Digest 5) support for Ubicom32 v3 architecture ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#define __ASM__ ++#include <asm/ip5000.h> ++ ++#ifndef RP ++#define RP A5 ++#endif ++ ++;***************************************************************************************** ++; The function prototypes ++;***************************************************************************************** ++; void md5_ip5k_init(void) ++; void md5_ip5k_transform(u32_t *data_input) ++; void md5_get_digest(u32_t *digest) ++ ++;***************************************************************************************** ++; Inputs ++;*****************************************************************************************; ++; data_input is the pointer to the block of data over which the digest will be calculated. ++; It should be word aligned. ++; ++; digest is the pointer to the block of data into which the digest (the output) will be written. ++; It should be word aligned. ++; ++ ++;***************************************************************************************** ++; Outputs ++;***************************************************************************************** ++; None ++ ++;***************************************************************************************** ++; An: Address Registers ++;***************************************************************************************** ++#define an_digest A3 ++#define an_data_input A3 ++#define an_security_block A4 ++ ++;***************************************************************************************** ++; Hash Constants ++;***************************************************************************************** ++#define HASH_MD5_IN0 0x01234567 ++#define HASH_MD5_IN1 0x89abcdef ++#define HASH_MD5_IN2 0xfedcba98 ++#define HASH_MD5_IN3 0x76543210 ++ ++#define HASH_SECURITY_BLOCK_CONTROL_INIT_NO_ENCYPTION 2 ++#define HASH_SECURITY_BLOCK_CONTROL_INIT_MD5 ((1 << 4) | HASH_SECURITY_BLOCK_CONTROL_INIT_NO_ENCYPTION) ++ ++;***************************************************************************************** ++; Hash related defines ++;***************************************************************************************** ++#define hash_control 0x00(an_security_block) ++#define hash_control_low 0x02(an_security_block) ++#define hash_status 0x04(an_security_block) ++ ++#define hash_input_0 0x30(an_security_block) ++#define hash_input_1 0x34(an_security_block) ++#define hash_input_2 0x38(an_security_block) ++#define hash_input_3 0x3c(an_security_block) ++#define hash_input_4 0x40(an_security_block) ++ ++#define hash_output_0 0x70(an_security_block) ++#define hash_output_0_low 0x72(an_security_block) ++#define hash_output_1 0x74(an_security_block) ++#define hash_output_1_low 0x76(an_security_block) ++#define hash_output_2 0x78(an_security_block) ++#define hash_output_2_low 0x7a(an_security_block) ++#define hash_output_3 0x7c(an_security_block) ++#define hash_output_3_low 0x7e(an_security_block) ++ ++;***************************************************************************************** ++; Assembly macros ++;***************************************************************************************** ++ ; C compiler reserves RP (A5) for return address during subroutine call. ++ ; Use RP to return to caller ++.macro call_return_macro ++ calli RP, 0(RP) ++.endm ++ ++#if 0 ++;***************************************************************************************** ++; void md5_ip5k_init(void) ++; initialize the output registers of the hash module ++; ++ ;.section .text.md5_ip5k_init,"ax",@progbits ++ .section .text ++ .global _md5_ip5k_init ++ .func md5_ip5k_init, _md5_ip5k_init ++ ++_md5_ip5k_init: ++ moveai an_security_block, #SECURITY_BASE_EFFECTIVE_ADDRESS ++ ++ movei hash_control, #%hi(HASH_SECURITY_BLOCK_CONTROL_INIT_MD5) ++ movei hash_control_low, #%lo(HASH_SECURITY_BLOCK_CONTROL_INIT_MD5) ++ ++ movei hash_output_0, #%hi(HASH_MD5_IN0) ++ movei hash_output_0_low, #%lo(HASH_MD5_IN0) ++ ++ movei hash_output_1, #%hi(HASH_MD5_IN1) ++ movei hash_output_1_low, #%lo(HASH_MD5_IN1) ++ ++ movei hash_output_2, #%hi(HASH_MD5_IN2) ++ movei hash_output_2_low, #%lo(HASH_MD5_IN2) ++ ++ movei hash_output_3, #%hi(HASH_MD5_IN3) ++ movei hash_output_3_low, #%lo(HASH_MD5_IN3) ++ ++ call_return_macro ++ .endfunc ++#endif ++ ++;***************************************************************************************** ++; void md5_ip5k_init_digest(u32_t *hash_input) ++; initialize the output registers of the hash module ++ ++ ;.section .text.md5_ip5k_init_digest,"ax",@progbits ++ .section .text ++ .global _md5_ip5k_init_digest ++ .func md5_ip5k_init_digest, _md5_ip5k_init_digest ++ ++_md5_ip5k_init_digest: ++ movea an_data_input, D0 ++ ++ moveai an_security_block, #SECURITY_BASE_EFFECTIVE_ADDRESS ++ ++ movei hash_control, #%hi(HASH_SECURITY_BLOCK_CONTROL_INIT_MD5) ++ movei hash_control_low, #%lo(HASH_SECURITY_BLOCK_CONTROL_INIT_MD5) ++ ++ move.4 hash_output_0, (an_data_input)4++ ++ move.4 hash_output_1, (an_data_input)4++ ++ move.4 hash_output_2, (an_data_input)4++ ++ move.4 hash_output_3, (an_data_input)4++ ++ ++ call_return_macro ++ .endfunc ++ ++;***************************************************************************************** ++; void md5_ip5k_transform(u32_t *data_input) ++; performs intermediate transformation step for the hash calculation ++; ++ ;.sect .text.md5_ip5k_transform,"ax",@progbits ++ .section .text ++ .global _md5_ip5k_transform ++ .func md5_ip5k_transform, _md5_ip5k_transform ++ ++_md5_ip5k_transform: ++ movea an_data_input, D0 ++ ++ moveai an_security_block, #SECURITY_BASE_EFFECTIVE_ADDRESS ++ ++ ; Write the first 128bits (16 bytes) ++ move.4 hash_input_0, (an_data_input)4++ ++ move.4 hash_input_1, (an_data_input)4++ ++ move.4 hash_input_2, (an_data_input)4++ ++ move.4 hash_input_3, (an_data_input)4++ ++ move.4 hash_input_4, D0 ++ ++ move.4 hash_input_0, (an_data_input)4++ ++ move.4 hash_input_1, (an_data_input)4++ ++ move.4 hash_input_2, (an_data_input)4++ ++ move.4 hash_input_3, (an_data_input)4++ ++ move.4 hash_input_4, D0 ++ ++ move.4 hash_input_0, (an_data_input)4++ ++ move.4 hash_input_1, (an_data_input)4++ ++ move.4 hash_input_2, (an_data_input)4++ ++ move.4 hash_input_3, (an_data_input)4++ ++ move.4 hash_input_4, D0 ++ ++ move.4 hash_input_0, (an_data_input)4++ ++ move.4 hash_input_1, (an_data_input)4++ ++ move.4 hash_input_2, (an_data_input)4++ ++ move.4 hash_input_3, (an_data_input)4++ ++ move.4 hash_input_4, D0 ++ ++ pipe_flush 0 ++ ++md5_ip5k_transform_wait: ++ ; wait for the module to calculate the output hash ++ btst hash_status, #0 ++ jmpne.f md5_ip5k_transform_wait ++ ++ call_return_macro ++ .endfunc ++ ++;***************************************************************************************** ++; void md5_ip5k_get_digest(u32_t *digest) ++; Return the hash of the input data ++; ++ ;.sect .text.md5_get_digest,"ax",@progbits ++ .section .text ++ .global _md5_ip5k_get_digest ++ .func md5_ip5k_get_digest, _md5_ip5k_get_digest ++ ++_md5_ip5k_get_digest: ++ movea an_digest, D0 ++ ++ moveai an_security_block, #SECURITY_BASE_EFFECTIVE_ADDRESS ++ ++ ; we have finished ++ move.4 0(an_digest), hash_output_0 ++ move.4 4(an_digest), hash_output_1 ++ move.4 8(an_digest), hash_output_2 ++ move.4 12(an_digest), hash_output_3 ++ ++ call_return_macro ++ .endfunc ++ +--- /dev/null ++++ b/arch/ubicom32/crypto/md5_ubicom32.c +@@ -0,0 +1,200 @@ ++/* ++ * arch/ubicom32/crypto/md5_ubicom32.c ++ * Ubicom32 implementation of the MD5 Secure Hash Algorithm ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/crypto.h> ++ ++#include "crypto_ubicom32.h" ++ ++#define MD5_DIGEST_SIZE 16 ++#define MD5_BLOCK_SIZE 64 ++#define MD5_HASH_WORDS 4 ++ ++extern void _md5_ip5k_init_digest(u32_t *digest); ++extern void _md5_ip5k_transform(u32_t *data_input); ++extern void _md5_ip5k_get_digest(u32_t *digest); ++ ++struct ubicom32_md5_ctx { ++ u64 count; /* message length */ ++ u32 state[MD5_HASH_WORDS]; ++ u8 buf[2 * MD5_BLOCK_SIZE]; ++}; ++ ++static void md5_init(struct crypto_tfm *tfm) ++{ ++ struct ubicom32_md5_ctx *mctx = crypto_tfm_ctx(tfm); ++ mctx->state[0] = 0x01234567; ++ mctx->state[1] = 0x89abcdef; ++ mctx->state[2] = 0xfedcba98; ++ mctx->state[3] = 0x76543210; ++ ++ mctx->count = 0; ++} ++ ++static inline void _md5_process(u32 *digest, const u8 *data) ++{ ++ _md5_ip5k_transform((u32 *)data); ++} ++ ++static void md5_update(struct crypto_tfm *tfm, const u8 *data, ++ unsigned int len) ++{ ++ struct ubicom32_md5_ctx *mctx = crypto_tfm_ctx(tfm); ++ int index, clen; ++ ++ /* how much is already in the buffer? */ ++ index = mctx->count & 0x3f; ++ ++ mctx->count += len; ++ ++ if (index + len < MD5_BLOCK_SIZE) { ++ goto store_only; ++ } ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ ++ /* init digest set ctrl register too */ ++ _md5_ip5k_init_digest(mctx->state); ++ ++ if (unlikely(index == 0 && SEC_ALIGNED(data))) { ++fast_process: ++ while (len >= MD5_BLOCK_SIZE) { ++ _md5_process(mctx->state, data); ++ data += MD5_BLOCK_SIZE; ++ len -= MD5_BLOCK_SIZE; ++ } ++ goto store; ++ } ++ ++ /* process one stored block */ ++ if (index) { ++ clen = MD5_BLOCK_SIZE - index; ++ memcpy(mctx->buf + index, data, clen); ++ _md5_process(mctx->state, mctx->buf); ++ data += clen; ++ len -= clen; ++ index = 0; ++ } ++ ++ if (likely(SEC_ALIGNED(data))) { ++ goto fast_process; ++ } ++ ++ /* process as many blocks as possible */ ++ while (len >= MD5_BLOCK_SIZE) { ++ memcpy(mctx->buf, data, MD5_BLOCK_SIZE); ++ _md5_process(mctx->state, mctx->buf); ++ data += MD5_BLOCK_SIZE; ++ len -= MD5_BLOCK_SIZE; ++ } ++ ++store: ++ _md5_ip5k_get_digest(mctx->state); ++ hw_crypto_unlock(); ++ ++store_only: ++ /* anything left? */ ++ if (len) ++ memcpy(mctx->buf + index , data, len); ++} ++ ++/* Add padding and return the message digest. */ ++static void md5_final(struct crypto_tfm *tfm, u8 *out) ++{ ++ struct ubicom32_md5_ctx *mctx = crypto_tfm_ctx(tfm); ++ u32 bits[2]; ++ unsigned int index, end; ++ ++ /* must perform manual padding */ ++ index = mctx->count & 0x3f; ++ end = (index < 56) ? MD5_BLOCK_SIZE : (2 * MD5_BLOCK_SIZE); ++ ++ /* start pad with 1 */ ++ mctx->buf[index] = 0x80; ++ ++ /* pad with zeros */ ++ index++; ++ memset(mctx->buf + index, 0x00, end - index - 8); ++ ++ /* append message length */ ++ bits[0] = mctx->count << 3; ++ bits[1] = mctx->count >> 29; ++ __cpu_to_le32s(bits); ++ __cpu_to_le32s(bits + 1); ++ ++ memcpy(mctx->buf + end - 8, &bits, sizeof(bits)); ++ ++ /* force to use the mctx->buf and ignore the partial buf */ ++ mctx->count = mctx->count & ~0x3f; ++ md5_update(tfm, mctx->buf, end); ++ ++ /* copy digest to out */ ++ memcpy(out, mctx->state, MD5_DIGEST_SIZE); ++ ++ /* wipe context */ ++ memset(mctx, 0, sizeof *mctx); ++} ++ ++static struct crypto_alg alg = { ++ .cra_name = "md5", ++ .cra_driver_name= "md5-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_DIGEST, ++ .cra_blocksize = MD5_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_md5_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(alg.cra_list), ++ .cra_u = { ++ .digest = { ++ .dia_digestsize = MD5_DIGEST_SIZE, ++ .dia_init = md5_init, ++ .dia_update = md5_update, ++ .dia_final = md5_final, ++ } ++ } ++}; ++ ++static int __init init(void) ++{ ++ hw_crypto_init(); ++ return crypto_register_alg(&alg); ++} ++ ++static void __exit fini(void) ++{ ++ crypto_unregister_alg(&alg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_ALIAS("md5"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MD5 Secure Hash Algorithm"); +--- /dev/null ++++ b/arch/ubicom32/crypto/sha1_ubicom32_asm.S +@@ -0,0 +1,244 @@ ++/* ++ * arch/ubicom32/crypto/sha1_ubicom32_asm.S ++ * SHA1 hash support for Ubicom32 architecture V3. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#define __ASM__ ++#include <asm/ip5000.h> ++ ++#ifndef RP ++#define RP A5 ++#endif ++ ++;***************************************************************************************** ++; The function prototype ++;***************************************************************************************** ++; void sha1_ip5k_init(void) ++; void sha1_ip5k_transform(u32_t *data_input) ++; void sha1_ip5k_output(u32_t *digest) ++ ++;***************************************************************************************** ++; Inputs ++;***************************************************************************************** ++; data_input is the pointer to the block of data over which the digest will be calculated. ++; It should be word aligned. ++; ++; digest is the pointer to the block of data into which the digest (the output) will be written. ++; It should be word aligned. ++; ++ ++;***************************************************************************************** ++; Outputs ++;***************************************************************************************** ++; None ++ ++;***************************************************************************************** ++; Hash Constants ++;***************************************************************************************** ++#define HASH_SHA1_IN0 0x67452301 ++#define HASH_SHA1_IN1 0xefcdab89 ++#define HASH_SHA1_IN2 0x98badcfe ++#define HASH_SHA1_IN3 0x10325476 ++#define HASH_SHA1_IN4 0xc3d2e1f0 ++ ++#define HASH_SECURITY_BLOCK_CONTROL_INIT_NO_ENCYPTION 2 ++#define HASH_SECURITY_BLOCK_CONTROL_INIT_SHA1 ((1 << 5) | HASH_SECURITY_BLOCK_CONTROL_INIT_NO_ENCYPTION) ++ ++;***************************************************************************************** ++; An: Address Registers ++;***************************************************************************************** ++#define an_digest a4 ++#define an_data_input a4 ++#define an_security_block a3 ++ ++;***************************************************************************************** ++; Hash related defines ++;***************************************************************************************** ++#define hash_control 0x00(an_security_block) ++#define hash_control_low 0x02(an_security_block) ++#define hash_status 0x04(an_security_block) ++ ++#define hash_input_0 0x30(an_security_block) ++#define hash_input_1 0x34(an_security_block) ++#define hash_input_2 0x38(an_security_block) ++#define hash_input_3 0x3c(an_security_block) ++#define hash_input_4 0x40(an_security_block) ++ ++#define hash_output_0 0x70(an_security_block) ++#define hash_output_0_low 0x72(an_security_block) ++#define hash_output_1 0x74(an_security_block) ++#define hash_output_1_low 0x76(an_security_block) ++#define hash_output_2 0x78(an_security_block) ++#define hash_output_2_low 0x7a(an_security_block) ++#define hash_output_3 0x7c(an_security_block) ++#define hash_output_3_low 0x7e(an_security_block) ++#define hash_output_4 0x80(an_security_block) ++#define hash_output_4_low 0x82(an_security_block) ++ ++;***************************************************************************************** ++; Assembly macros ++;***************************************************************************************** ++ ; C compiler reserves RP (A5) for return address during subroutine call. ++ ; Use RP to return to caller ++.macro call_return_macro ++ calli RP, 0(RP) ++.endm ++ ++;***************************************************************************************** ++; void sha1_ip5k_init(void) ++; initialize the output registers of the hash module ++ ++ ;.section .text.sha1_ip5k_init,"ax",@progbits ++ .section .ocm_text,"ax",@progbits ++ .global _sha1_ip5k_init ++ .func sha1_ip5k_init, _sha1_ip5k_init ++ ++_sha1_ip5k_init: ++ moveai an_security_block, #SECURITY_BASE_EFFECTIVE_ADDRESS ++ ++ movei hash_control, #%hi(HASH_SECURITY_BLOCK_CONTROL_INIT_SHA1) ++ movei hash_control_low, #%lo(HASH_SECURITY_BLOCK_CONTROL_INIT_SHA1) ++ ++ movei hash_output_0, #%hi(HASH_SHA1_IN0) ++ movei hash_output_0_low, #%lo(HASH_SHA1_IN0) ++ ++ movei hash_output_1, #%hi(HASH_SHA1_IN1) ++ movei hash_output_1_low, #%lo(HASH_SHA1_IN1) ++ ++ movei hash_output_2, #%hi(HASH_SHA1_IN2) ++ movei hash_output_2_low, #%lo(HASH_SHA1_IN2) ++ ++ movei hash_output_3, #%hi(HASH_SHA1_IN3) ++ movei hash_output_3_low, #%lo(HASH_SHA1_IN3) ++ ++ movei hash_output_4, #%hi(HASH_SHA1_IN4) ++ movei hash_output_4_low, #%lo(HASH_SHA1_IN4) ++ ++ call_return_macro ++ .endfunc ++ ++;***************************************************************************************** ++; void sha1_ip5k_init_digest(u32_t *hash_input) ++; initialize the output registers of the hash module ++ ++ ;.section .text.sha1_ip5k_init_digest,"ax",@progbits ++ .section .ocm_text,"ax",@progbits ++ .global _sha1_ip5k_init_digest ++ .func sha1_ip5k_init_digest, _sha1_ip5k_init_digest ++ ++_sha1_ip5k_init_digest: ++ movea an_data_input, D0 ++ ++ moveai an_security_block, #SECURITY_BASE_EFFECTIVE_ADDRESS ++ ++ movei hash_control, #%hi(HASH_SECURITY_BLOCK_CONTROL_INIT_SHA1) ++ movei hash_control_low, #%lo(HASH_SECURITY_BLOCK_CONTROL_INIT_SHA1) ++ ++ move.4 hash_output_0, (an_data_input)4++ ++ move.4 hash_output_1, (an_data_input)4++ ++ move.4 hash_output_2, (an_data_input)4++ ++ move.4 hash_output_3, (an_data_input)4++ ++ move.4 hash_output_4, (an_data_input)4++ ++ ++ call_return_macro ++ .endfunc ++ ++;***************************************************************************************** ++; void sha1_ip5k_transform(u32_t *data_input) ++; performs intermediate transformation step for the hash calculation ++ ++ ;.section .text.sha1_ip5k_transform,"ax",@progbits ++ .section .ocm_text,"ax",@progbits ++ .global _sha1_ip5k_transform ++ .func sha1_ip5k_transform, _sha1_ip5k_transform ++ ++_sha1_ip5k_transform: ++ movea an_data_input, D0 ++ ++ moveai an_security_block, #SECURITY_BASE_EFFECTIVE_ADDRESS ++ ++ ; Write the first 128bits (16 bytes) ++ move.4 hash_input_0, (an_data_input)4++ ++ move.4 hash_input_1, (an_data_input)4++ ++ move.4 hash_input_2, (an_data_input)4++ ++ move.4 hash_input_3, (an_data_input)4++ ++ move.4 hash_input_4, D0 ++ ++ move.4 hash_input_0, (an_data_input)4++ ++ move.4 hash_input_1, (an_data_input)4++ ++ move.4 hash_input_2, (an_data_input)4++ ++ move.4 hash_input_3, (an_data_input)4++ ++ move.4 hash_input_4, D0 ++ ++ move.4 hash_input_0, (an_data_input)4++ ++ move.4 hash_input_1, (an_data_input)4++ ++ move.4 hash_input_2, (an_data_input)4++ ++ move.4 hash_input_3, (an_data_input)4++ ++ move.4 hash_input_4, D0 ++ ++ move.4 hash_input_0, (an_data_input)4++ ++ move.4 hash_input_1, (an_data_input)4++ ++ move.4 hash_input_2, (an_data_input)4++ ++ move.4 hash_input_3, (an_data_input)4++ ++ move.4 hash_input_4, D0 ++ ++ pipe_flush 0 ++ ++sha1_ip5k_transform_wait: ++ ; wait for the module to calculate the output hash ++ btst hash_status, #0 ++ jmpne.f sha1_ip5k_transform_wait ++ ++ call_return_macro ++ .endfunc ++ ++;***************************************************************************************** ++; void sha1_ip5k_output(u32_t *digest) ++; Return the hash of the input data ++ ++ ;.section .text.sha1_ip5k_output,"ax",@progbits ++ .section .ocm_text,"ax",@progbits ++ .global _sha1_ip5k_output ++ .func sha1_ip5k_output, _sha1_ip5k_output ++ ++_sha1_ip5k_output: ++ movea an_digest, D0 ++ ++ moveai an_security_block, #SECURITY_BASE_EFFECTIVE_ADDRESS ++ ++ ; we have finished ++ move.4 0(an_digest), hash_output_0 ++ move.4 4(an_digest), hash_output_1 ++ move.4 8(an_digest), hash_output_2 ++ move.4 12(an_digest), hash_output_3 ++ move.4 16(an_digest), hash_output_4 ++ ++ call_return_macro ++ .endfunc ++ ++;***************************************************************************************** ++;END ;End of program code ++;***************************************************************************************** +--- /dev/null ++++ b/arch/ubicom32/crypto/sha1_ubicom32.c +@@ -0,0 +1,354 @@ ++/* ++ * arch/ubicom32/crypto/sha1_ubicom32.c ++ * Ubicom32 implementation of the SHA1 Secure Hash Algorithm. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/crypto.h> ++#include <crypto/sha.h> ++#include <asm/linkage.h> ++ ++#include "crypto_ubicom32.h" ++#define HASH_SECURITY_BLOCK_CONTROL_INIT_NO_ENCYPTION 2 ++#define HASH_SECURITY_BLOCK_CONTROL_INIT_SHA1 ((1 << 5) | HASH_SECURITY_BLOCK_CONTROL_INIT_NO_ENCYPTION) ++ ++struct ubicom32_sha1_ctx { ++ u64 count; /* message length */ ++ u32 state[5]; ++ u8 buf[2 * SHA1_BLOCK_SIZE]; ++}; ++ ++static inline void sha1_clear_2ws(u8 *buf, int wc) ++{ ++ asm volatile ( ++ "1: move.4 (%0)4++, #0 \n\t" ++ " move.4 (%0)4++, #0 \n\t" ++ " sub.4 %1, #2, %1 \n\t" ++ " jmple.f 1b \n\t" ++ : ++ : "a" (buf), "d" (wc) ++ : "cc" ++ ); ++} ++ ++/* only wipe out count, state, and 1st half of buf - 9 bytes at most */ ++#define sha1_wipe_out(sctx) sha1_clear_2ws((u8 *)sctx, 2 + 5 + 16 - 2) ++ ++static inline void sha1_init_digest(u32 *digest) ++{ ++ hw_crypto_set_ctrl(HASH_SECURITY_BLOCK_CONTROL_INIT_SHA1); ++ asm volatile ( ++ " ; move digests to hash_output regs \n\t" ++ " move.4 0x70(%0), 0x0(%1) \n\t" ++ " move.4 0x74(%0), 0x4(%1) \n\t" ++ " move.4 0x78(%0), 0x8(%1) \n\t" ++ " move.4 0x7c(%0), 0xc(%1) \n\t" ++ " move.4 0x80(%0), 0x10(%1) \n\t" ++ : ++ : "a" (SEC_BASE), "a" (digest) ++ ); ++} ++ ++static inline void sha1_transform_feed(const u8 *in) ++{ ++ asm volatile ( ++ " ; write the 1st 16 bytes \n\t" ++ " move.4 0x30(%0), 0x0(%1) \n\t" ++ " move.4 0x34(%0), 0x4(%1) \n\t" ++ " move.4 0x38(%0), 0x8(%1) \n\t" ++ " move.4 0x3c(%0), 0xc(%1) \n\t" ++ " move.4 0x40(%0), %1 \n\t" ++ " ; write the 2nd 16 bytes \n\t" ++ " move.4 0x30(%0), 0x10(%1) \n\t" ++ " move.4 0x34(%0), 0x14(%1) \n\t" ++ " move.4 0x38(%0), 0x18(%1) \n\t" ++ " move.4 0x3c(%0), 0x1c(%1) \n\t" ++ " move.4 0x40(%0), %1 \n\t" ++ " ; write the 3rd 16 bytes \n\t" ++ " move.4 0x30(%0), 0x20(%1) \n\t" ++ " move.4 0x34(%0), 0x24(%1) \n\t" ++ " move.4 0x38(%0), 0x28(%1) \n\t" ++ " move.4 0x3c(%0), 0x2c(%1) \n\t" ++ " move.4 0x40(%0), %1 \n\t" ++ " ; write the 4th 16 bytes \n\t" ++ " move.4 0x30(%0), 0x30(%1) \n\t" ++ " move.4 0x34(%0), 0x34(%1) \n\t" ++ " move.4 0x38(%0), 0x38(%1) \n\t" ++ " move.4 0x3c(%0), 0x3c(%1) \n\t" ++ " move.4 0x40(%0), %1 \n\t" ++ " pipe_flush 0 \n\t" ++ : ++ : "a"(SEC_BASE), "a"(in) ++ ); ++} ++ ++static inline void sha1_transform_wait(void) ++{ ++ asm volatile ( ++ " btst 0x04(%0), #0 \n\t" ++ " jmpne.f -4 \n\t" ++ : ++ : "a"(SEC_BASE) ++ : "cc" ++ ); ++} ++ ++static inline void sha1_output_digest(u32 *digest) ++{ ++ asm volatile ( ++ " move.4 0x0(%1), 0x70(%0) \n\t" ++ " move.4 0x4(%1), 0x74(%0) \n\t" ++ " move.4 0x8(%1), 0x78(%0) \n\t" ++ " move.4 0xc(%1), 0x7c(%0) \n\t" ++ " move.4 0x10(%1), 0x80(%0) \n\t" ++ : ++ : "a" (SEC_BASE), "a" (digest) ++ ); ++} ++ ++static __ocm_text void sha1_init(struct crypto_tfm *tfm) ++{ ++ struct ubicom32_sha1_ctx *sctx = crypto_tfm_ctx(tfm); ++ ++ sctx->state[0] = SHA1_H0; ++ sctx->state[1] = SHA1_H1; ++ sctx->state[2] = SHA1_H2; ++ sctx->state[3] = SHA1_H3; ++ sctx->state[4] = SHA1_H4; ++ sctx->count = 0; ++} ++ ++static void __ocm_text sha1_update(struct crypto_tfm *tfm, const u8 *data, ++ unsigned int len) ++{ ++ struct ubicom32_sha1_ctx *sctx = crypto_tfm_ctx(tfm); ++ int index, clen; ++ ++ /* how much is already in the buffer? */ ++ index = sctx->count & 0x3f; ++ ++ sctx->count += len; ++ ++ if (index + len < SHA1_BLOCK_SIZE) { ++ goto store_only; ++ } ++ ++ hw_crypto_lock(); ++ hw_crypto_check(); ++ ++ /* init digest set ctrl register too */ ++ sha1_init_digest(sctx->state); ++ ++ if (unlikely(index == 0 && SEC_ALIGNED(data))) { ++fast_process: ++#if CRYPTO_UBICOM32_LOOP_ASM ++ if (likely(len >= SHA1_BLOCK_SIZE)) { ++ register unsigned int cnt = len >> 6; // loop = len / 64; ++ sha1_transform_feed(data); ++ data += SHA1_BLOCK_SIZE; ++ ++ /* cnt is pre-decremented in the loop */ ++ asm volatile ( ++ "; while (--loop): work on 2nd block \n\t" ++ "1: add.4 %2, #-1, %2 \n\t" ++ " jmpeq.f 5f \n\t" ++ " \n\t" ++ " ; write the 1st 16 bytes \n\t" ++ " move.4 0x30(%1), (%0)4++ \n\t" ++ " move.4 0x34(%1), (%0)4++ \n\t" ++ " move.4 0x38(%1), (%0)4++ \n\t" ++ " move.4 0x3c(%1), (%0)4++ \n\t" ++ " ; can not kick off hw before it \n\t" ++ " ; is done with the prev block \n\t" ++ " \n\t" ++ " btst 0x04(%1), #0 \n\t" ++ " jmpne.f -4 \n\t" ++ " \n\t" ++ " ; tell hw to load 1st 16 bytes \n\t" ++ " move.4 0x40(%1), %2 \n\t" ++ " \n\t" ++ " ; write the 2nd 16 bytes \n\t" ++ " move.4 0x30(%1), (%0)4++ \n\t" ++ " move.4 0x34(%1), (%0)4++ \n\t" ++ " move.4 0x38(%1), (%0)4++ \n\t" ++ " move.4 0x3c(%1), (%0)4++ \n\t" ++ " move.4 0x40(%1), %2 \n\t" ++ " \n\t" ++ " ; write the 3rd 16 bytes \n\t" ++ " move.4 0x30(%1), (%0)4++ \n\t" ++ " move.4 0x34(%1), (%0)4++ \n\t" ++ " move.4 0x38(%1), (%0)4++ \n\t" ++ " move.4 0x3c(%1), (%0)4++ \n\t" ++ " move.4 0x40(%1), %2 \n\t" ++ " \n\t" ++ " ; write the 4th 16 bytes \n\t" ++ " move.4 0x30(%1), (%0)4++ \n\t" ++ " move.4 0x34(%1), (%0)4++ \n\t" ++ " move.4 0x38(%1), (%0)4++ \n\t" ++ " move.4 0x3c(%1), (%0)4++ \n\t" ++ " move.4 0x40(%1), %2 \n\t" ++ " \n\t" ++ "; no need flush, enough insts \n\t" ++ "; before next hw wait \n\t" ++ " \n\t" ++ "; go back to loop \n\t" ++ " jmpt 1b \n\t" ++ " \n\t" ++ "; wait hw for last block \n\t" ++ "5: btst 0x04(%1), #0 \n\t" ++ " jmpne.f -4 \n\t" ++ " \n\t" ++ : "+a" (data) ++ : "a"( SEC_BASE), "d" (cnt) ++ : "cc" ++ ); ++ ++ len = len & (64 - 1); ++ } ++#else ++ while (likely(len >= SHA1_BLOCK_SIZE)) { ++ sha1_transform_feed(data); ++ data += SHA1_BLOCK_SIZE; ++ len -= SHA1_BLOCK_SIZE; ++ sha1_transform_wait(); ++ } ++#endif ++ goto store; ++ } ++ ++ /* process one stored block */ ++ if (index) { ++ clen = SHA1_BLOCK_SIZE - index; ++ memcpy(sctx->buf + index, data, clen); ++ sha1_transform_feed(sctx->buf); ++ data += clen; ++ len -= clen; ++ index = 0; ++ sha1_transform_wait(); ++ } ++ ++ if (likely(SEC_ALIGNED(data))) { ++ goto fast_process; ++ } ++ ++ /* process as many blocks as possible */ ++ if (likely(len >= SHA1_BLOCK_SIZE)) { ++ memcpy(sctx->buf, data, SHA1_BLOCK_SIZE); ++ do { ++ sha1_transform_feed(sctx->buf); ++ data += SHA1_BLOCK_SIZE; ++ len -= SHA1_BLOCK_SIZE; ++ if (likely(len >= SHA1_BLOCK_SIZE)) { ++ memcpy(sctx->buf, data, SHA1_BLOCK_SIZE); ++ sha1_transform_wait(); ++ continue; ++ } ++ /* it is the last block */ ++ sha1_transform_wait(); ++ break; ++ } while (1); ++ } ++ ++store: ++ sha1_output_digest(sctx->state); ++ hw_crypto_unlock(); ++ ++store_only: ++ /* anything left? */ ++ if (len) ++ memcpy(sctx->buf + index , data, len); ++} ++ ++/* Add padding and return the message digest. */ ++static void __ocm_text sha1_final(struct crypto_tfm *tfm, u8 *out) ++{ ++ struct ubicom32_sha1_ctx *sctx = crypto_tfm_ctx(tfm); ++ u64 bits; ++ unsigned int index, end; ++ ++ /* must perform manual padding */ ++ index = sctx->count & 0x3f; ++ end = (index < 56) ? SHA1_BLOCK_SIZE : (2 * SHA1_BLOCK_SIZE); ++ ++ /* start pad with 1 */ ++ sctx->buf[index] = 0x80; ++ ++ /* pad with zeros */ ++ index++; ++ memset(sctx->buf + index, 0x00, end - index - 8); ++ ++ /* append message length */ ++ bits = sctx->count << 3 ; ++ SEC_COPY_2W(sctx->buf + end - 8, &bits); ++ ++ /* force to use the sctx->buf and ignore the partial buf */ ++ sctx->count = sctx->count & ~0x3f; ++ sha1_update(tfm, sctx->buf, end); ++ ++ /* copy digest to out */ ++ SEC_COPY_5W(out, sctx->state); ++ ++ /* wipe context */ ++ sha1_wipe_out(sctx); ++} ++ ++static struct crypto_alg alg = { ++ .cra_name = "sha1", ++ .cra_driver_name= "sha1-ubicom32", ++ .cra_priority = CRYPTO_UBICOM32_PRIORITY, ++ .cra_flags = CRYPTO_ALG_TYPE_DIGEST, ++ .cra_blocksize = SHA1_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ubicom32_sha1_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(alg.cra_list), ++ .cra_u = { ++ .digest = { ++ .dia_digestsize = SHA1_DIGEST_SIZE, ++ .dia_init = sha1_init, ++ .dia_update = sha1_update, ++ .dia_final = sha1_final, ++ } ++ } ++}; ++ ++static int __init init(void) ++{ ++ hw_crypto_init(); ++ return crypto_register_alg(&alg); ++} ++ ++static void __exit fini(void) ++{ ++ crypto_unregister_alg(&alg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_ALIAS("sha1"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm"); +--- /dev/null ++++ b/arch/ubicom32/include/asm/a.out.h +@@ -0,0 +1,47 @@ ++/* ++ * arch/ubicom32/include/asm/a.out.h ++ * Definitions for Ubicom32 a.out executable format. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_A_OUT_H ++#define _ASM_UBICOM32_A_OUT_H ++ ++struct exec ++{ ++ unsigned long a_info; /* Use macros N_MAGIC, etc for access */ ++ unsigned a_text; /* length of text, in bytes */ ++ unsigned a_data; /* length of data, in bytes */ ++ unsigned a_bss; /* length of uninitialized data area for file, in bytes */ ++ unsigned a_syms; /* length of symbol table data in file, in bytes */ ++ unsigned a_entry; /* start address */ ++ unsigned a_trsize; /* length of relocation info for text, in bytes */ ++ unsigned a_drsize; /* length of relocation info for data, in bytes */ ++}; ++ ++#define N_TRSIZE(a) ((a).a_trsize) ++#define N_DRSIZE(a) ((a).a_drsize) ++#define N_SYMSIZE(a) ((a).a_syms) ++ ++#endif /* _ASM_UBICOM32_A_OUT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/atomic.h +@@ -0,0 +1,352 @@ ++/* ++ * arch/ubicom32/include/asm/atomic.h ++ * Atomic operations definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_ATOMIC_H ++#define _ASM_UBICOM32_ATOMIC_H ++ ++#include <asm/system.h> ++#include <asm/ubicom32-common.h> ++#include <asm/types.h> ++ ++/* ++ * Most instructions on the Ubicom32 processor are atomic in that they ++ * execute in one clock cycle. However, Linux has several operations ++ * (e.g. compare and swap) which will require more than a single instruction ++ * to perform. To achieve this, the Ubicom32 processor uses a single ++ * global bit in a scratchpad register as a critical section lock. All ++ * atomic operations acquire this lock. ++ * ++ * NOTE: To AVOID DEADLOCK(s), the atomic lock must only be used for atomic ++ * operations or by the ldsr to avoid disabling a thread performing an atomic ++ * operation. ++ * ++ * Do not attempt to disable interrupts while holding the atomic operations ++ * lock or you will DEADLOCK the system. ++ */ ++ ++typedef struct { ++ volatile int counter; ++} atomic_t; ++ ++#define ATOMIC_INIT(i) { (i) } ++ ++/* ++ * __atomic_add() ++ * Add i to v and return the result. ++ */ ++static inline void __atomic_add(int i, atomic_t *v) ++{ ++ atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ vt->counter += i; ++ __atomic_lock_release(); ++} ++ ++/* ++ * __atomic_sub() ++ * Subtract i from v and return the result. ++ */ ++static inline void __atomic_sub(int i, atomic_t *v) ++{ ++ atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ vt->counter -= i; ++ __atomic_lock_release(); ++} ++ ++/* ++ * __atomic_add_return() ++ * Add i to v and return the result. ++ * ++ * The implementation here looks rather odd because we appear to be doing ++ * the addition twice. In fact that's exactly what we're doing but with ++ * the ubicom32 instruction set we can do the inner load and add with two ++ * instructions whereas generating both the atomic result and the "ret" ++ * result requires three instructions. The second add is generally only as ++ * costly as a move instruction and in cases where we compare the result ++ * with a constant the compiler can fold two constant values and do a ++ * single instruction, thus saving an instruction overall! ++ * ++ * At the worst we save one instruction inside the atomic lock. ++ */ ++static inline int __atomic_add_return(int i, atomic_t *v) ++{ ++ int ret; ++ atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ ret = vt->counter; ++ vt->counter = ret + i; ++ __atomic_lock_release(); ++ ++ return ret + i; ++} ++ ++/* ++ * __atomic_sub_return() ++ * Subtract i from v and return the result. ++ * ++ * The implementation here looks rather odd because we appear to be doing ++ * the subtraction twice. In fact that's exactly what we're doing but with ++ * the ubicom32 instruction set we can do the inner load and sub with two ++ * instructions whereas generating both the atomic result and the "ret" ++ * result requires three instructions. The second sub is generally only as ++ * costly as a move instruction and in cases where we compare the result ++ * with a constant the compiler can fold two constant values and do a ++ * single instruction, thus saving an instruction overall! ++ * ++ * At the worst we save one instruction inside the atomic lock. ++ */ ++static inline int __atomic_sub_return(int i, atomic_t *v) ++{ ++ int ret; ++ atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ ret = vt->counter; ++ vt->counter = ret - i; ++ __atomic_lock_release(); ++ ++ return ret - i; ++} ++ ++/* ++ * PUBLIC API FOR ATOMIC! ++ */ ++#define atomic_add(i,v) (__atomic_add( ((int)i),(v))) ++#define atomic_sub(i,v) (__atomic_sub( ((int)i),(v))) ++#define atomic_inc(v) (__atomic_add( 1,(v))) ++#define atomic_dec(v) (__atomic_sub( 1,(v))) ++#define atomic_add_return(i,v) (__atomic_add_return( ((int)i),(v))) ++#define atomic_sub_return(i,v) (__atomic_sub_return( ((int)i),(v))) ++#define atomic_inc_return(v) (__atomic_add_return( 1,(v))) ++#define atomic_dec_return(v) (__atomic_sub_return( 1,(v))) ++#define atomic_inc_and_test(v) (atomic_inc_return(v) == 0) ++#define atomic_dec_and_test(v) (atomic_dec_return(v) == 0) ++#define atomic_add_negative(a, v) (atomic_add_return((a), (v)) < 0) ++#define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0) ++ ++/* ++ * atomic_read() ++ * Acquire the atomic lock and read the variable. ++ */ ++static inline int atomic_read(const atomic_t *v) ++{ ++ int ret; ++ const atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ ret = vt->counter; ++ __atomic_lock_release(); ++ ++ return ret; ++} ++ ++/* ++ * atomic_set() ++ * Acquire the atomic lock and set the variable. ++ */ ++static inline void atomic_set(atomic_t *v, int i) ++{ ++ atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ vt->counter = i; ++ __atomic_lock_release(); ++} ++ ++/* ++ * atomic_cmpxchg ++ * Acquire the atomic lock and exchange if current == old. ++ */ ++static inline int atomic_cmpxchg(atomic_t *v, int old, int new) ++{ ++ int prev; ++ atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ prev = vt->counter; ++ if (prev == old) { ++ vt->counter = new; ++ } ++ __atomic_lock_release(); ++ ++ return prev; ++} ++ ++/* ++ * atomic_xchg() ++ * Acquire the atomic lock and exchange values. ++ */ ++static inline int atomic_xchg(atomic_t *v, int new) ++{ ++ int prev; ++ atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ prev = vt->counter; ++ vt->counter = new; ++ __atomic_lock_release(); ++ ++ return prev; ++} ++ ++/* ++ * atomic_add_unless() ++ * Acquire the atomic lock and add a unless the value is u. ++ */ ++static inline int atomic_add_unless(atomic_t *v, int a, int u) ++{ ++ int prev; ++ atomic_t *vt = v; ++ ++ __atomic_lock_acquire(); ++ prev = vt->counter; ++ if (prev != u) { ++ vt->counter += a; ++ __atomic_lock_release(); ++ return 1; ++ } ++ ++ __atomic_lock_release(); ++ return 0; ++} ++ ++#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) ++ ++#include <asm-generic/atomic.h> ++ ++/* ++ * The following is not a real function. The compiler should remove the function ++ * call as long as the user does not pass in a size that __xchg and __cmpxchg ++ * are not prepared for. If the user does pass in an unknown size, the user ++ * will get a link time error. ++ * ++ * The no return is to prevent a compiler error that can occur when dealing with ++ * uninitialized variables. Given that the function doesn't exist there is no ++ * net effect (and if it did it would not return). ++ */ ++extern void __xchg_called_with_bad_pointer(void) __attribute__((noreturn)); ++ ++/* ++ * __xchg() ++ * Xchange *ptr for x atomically. ++ * ++ * Must be both locally atomic and atomic on SMP. Ubicom32 does not have an ++ * atomic exchange instruction so we use the global atomic_lock. ++ */ ++static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size) ++{ ++ unsigned long ret; ++ ++ __atomic_lock_acquire(); ++ ++ switch (size) { ++ case 1: ++ ret = *(volatile unsigned char *)ptr; ++ *(volatile unsigned char *)ptr = x; ++ break; ++ ++ case 2: ++ ret = *(volatile unsigned short *)ptr; ++ *(volatile unsigned short *)ptr = x; ++ break; ++ ++ case 4: ++ ret = *(volatile unsigned int *)ptr; ++ *(volatile unsigned int *)ptr = x; ++ break; ++ ++ default: ++ __xchg_called_with_bad_pointer(); ++ break; ++ } ++ __atomic_lock_release(); ++ return ret; ++} ++ ++#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) ++ ++/* ++ * __cmpxchg() ++ * Compare and Xchange *ptr for x atomically. ++ * ++ * Must be both locally atomic and atomic on SMP. Ubicom32 does not have an ++ * atomic exchange instruction so we use the global atomic_lock. ++ */ ++static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long next, int size) ++{ ++ unsigned long prev; ++ ++ __atomic_lock_acquire(); ++ switch (size) { ++ case 1: ++ prev = *(u8 *)ptr; ++ if (prev == old) { ++ *(u8 *)ptr = (u8)next; ++ } ++ break; ++ ++ case 2: ++ prev = *(u16 *)ptr; ++ if (prev == old) { ++ *(u16 *)ptr = (u16)next; ++ } ++ break; ++ ++ case 4: ++ prev = *(u32 *)ptr; ++ if (prev == old) { ++ *(u32 *)ptr = (u32)next; ++ } ++ break; ++ ++ default: ++ __xchg_called_with_bad_pointer(); ++ break; ++ } ++ __atomic_lock_release(); ++ return prev; ++} ++ ++/* ++ * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make ++ * them available. ++ */ ++#define cmpxchg_local(ptr, o, n) \ ++ ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), (unsigned long)(n), sizeof(*(ptr)))) ++ ++#define cmpxchg(ptr, o, n) __cmpxchg((ptr), (o), (n), sizeof(*(ptr))) ++ ++#define smp_mb__before_atomic_inc() asm volatile ("" : : : "memory") ++#define smp_mb__after_atomic_inc() asm volatile ("" : : : "memory") ++#define smp_mb__before_atomic_dec() asm volatile ("" : : : "memory") ++#define smp_mb__after_atomic_dec() asm volatile ("" : : : "memory") ++ ++#endif /* _ASM_UBICOM32_ATOMIC_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/audio_tio.h +@@ -0,0 +1,124 @@ ++/* ++ * arch/ubicom32/include/asm/audio_tio.h ++ * AudioTIO include file ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef _AUDIO_TIO_H ++#define _AUDIO_TIO_H ++ ++#include <asm/devtree.h> ++#include <asm/ubi32-pcm.h> ++ ++#define UBI32_AUDIO_INT_FLAG_MORE_SAMPLES 0x00000001 ++#define UBI32_AUDIO_INT_FLAG_COMMAND 0x00000002 ++ ++/* ++ * Commands the Primary OS sends to the audio device ++ */ ++enum ubi32_audio_command { ++ UBI32_AUDIO_CMD_NONE, ++ UBI32_AUDIO_CMD_START, ++ UBI32_AUDIO_CMD_STOP, ++ UBI32_AUDIO_CMD_PAUSE, ++ UBI32_AUDIO_CMD_RESUME, ++ UBI32_AUDIO_CMD_MUTE, ++ UBI32_AUDIO_CMD_UNMUTE, ++ UBI32_AUDIO_CMD_SETUP, ++ UBI32_AUDIO_CMD_ENABLE, ++ UBI32_AUDIO_CMD_DISABLE, ++}; ++ ++/* ++ * Flag bits passed in the registers ++ */ ++#define UBI32_CMD_SETUP_FLAG_LE (1 << 0) /* Use Little Endian Mode */ ++ ++/* ++ * Status bits that audio device can set to indicate reason ++ * for interrupting the Primary OS ++ */ ++#define UBI32_AUDIO_STATUS_PLAY_DMA0_REQUEST (1 << 0) /* Audio device needs samples in DMA0 for playback */ ++#define UBI32_AUDIO_STATUS_PLAY_DMA1_REQUEST (1 << 1) /* Audio device needs samples in DMA1 for playback */ ++ ++struct ubi32_audio_dma { ++ /* ++ * NOTE: The active flag shall only be SET by the producer and CLEARED ++ * by the consumer, NEVER the other way around. For playback, the ++ * Primary OS sets this flag and ipAudioTIO clears it. ++ * ++ * The producer shall not modify the ptr or ctr fields when the transfer ++ * is marked as active, as these are used by the consumer to do the ++ * transfer. ++ */ ++ volatile uint32_t active; /* Nonzero if data in ptr/ctr ready to be transferred */ ++ //volatile u32_t active; /* Nonzero if data in ptr/ctr ready to be transferred */ ++ volatile void *ptr; /* Pointer to data to be transferred */ ++ volatile uint32_t ctr; /* Counter: number of data units to transfer */ ++ //volatile u32_t ctr; /* Counter: number of data units to transfer */ ++}; ++ ++#define AUDIOTIONODE_CAP_BE (1 << 0) ++#define AUDIOTIONODE_CAP_LE (1 << 1) ++ ++/* ++ * Resource indices used to access IRQs via platform_get_resource ++ */ ++#define AUDIOTIO_MEM_RESOURCE 0 ++#define AUDIOTIO_TX_IRQ_RESOURCE 0 ++#define AUDIOTIO_RX_IRQ_RESOURCE 1 ++ ++#define AUDIOTIONODE_VERSION 5 ++struct audiotionode { ++ struct devtree_node dn; ++ uint32_t version; /* Version of this node */ ++ struct audiotioregs *regs; ++}; ++ ++#define AUDIOTIOREGS_VERSION 3 ++struct audiotioregs { ++ uint32_t version; ++ uint32_t caps; /* Capabilities of the driver */ ++ u32_t *sample_rates; /* Sample Rates supported by this driver */ ++ u32_t n_sample_rates; /* Number of sample rates supported by this driver */ ++ u32_t channel_mask; /* The channel configs supported by this driver (bit 1 = 1 channel, etc) */ ++ volatile uint32_t int_flags; /* Reason for interrupting audio device */ ++ volatile enum ubi32_audio_command command; /* Command from Primary OS */ ++ volatile uint32_t flags; /* Flag bits for this command */ ++ volatile uint32_t channels; /* Number of channels in stream */ ++ volatile uint32_t sample_rate; /* Sample rate */ ++ volatile uint32_t status; /* Status bits sent from AudioTIO to Primary OS */ ++ volatile void *current_read_pos; /* Position of next sample to be removed from Primary OS sample buffer */ ++ ++ /* ++ * These are the transfer requests. They are used in alternating ++ * order so that when ipAudioTIO is processing one request, the ++ * Primary OS can fill in the other one. ++ * ++ * NOTE: The active bit shall always be SET by the producer and ++ * CLEARED by the consumer, NEVER the other way around. ++ */ ++ struct ubi32_audio_dma playback_xfer_requests[2]; ++}; ++ ++extern struct platform_device * __init audio_tio_alloc(const char *driver_name, const char *node_name, int priv_size); ++ ++#define audio_tio_priv(pdev) (((struct ubi32pcm_platform_data *)(((struct platform_device *)(pdev))->dev.platform_data))->priv_data) ++#endif +--- /dev/null ++++ b/arch/ubicom32/include/asm/auxvec.h +@@ -0,0 +1,32 @@ ++/* ++ * arch/ubicom32/include/asm/auxvec.h ++ * Symbolic values for the entries in the auxiliary table ++ * put on the initial stack. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_AUXVEC_H ++#define _ASM_UBICOM32_AUXVEC_H ++ ++#endif /* _ASM_UBICOM32_AUXVEC_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/bitops.h +@@ -0,0 +1,171 @@ ++/* ++ * arch/ubicom32/include/asm/bitops.h ++ * Bit manipulation definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_BITOPS_H ++#define _ASM_UBICOM32_BITOPS_H ++ ++/* ++ * Copyright 1992, Linus Torvalds. ++ */ ++ ++#include <linux/compiler.h> ++#include <asm/byteorder.h> /* swab32 */ ++ ++#ifdef __KERNEL__ ++ ++#ifndef _LINUX_BITOPS_H ++#error only <linux/bitops.h> can be included directly ++#endif ++ ++#include <asm-generic/bitops/ffs.h> ++#include <asm-generic/bitops/__ffs.h> ++ ++#include <asm-generic/bitops/sched.h> ++#include <asm-generic/bitops/ffz.h> ++ ++#include <asm/ubicom32-common.h> ++ ++static inline void set_bit(int bit, volatile unsigned long *p) ++{ ++ unsigned long mask = 1UL << (bit & 31); ++ ++ p += bit >> 5; ++ ++ __atomic_lock_acquire(); ++ *p |= mask; ++ __atomic_lock_release(); ++} ++ ++static inline void clear_bit(int bit, volatile unsigned long *p) ++{ ++ unsigned long mask = 1UL << (bit & 31); ++ ++ p += bit >> 5; ++ ++ __atomic_lock_acquire(); ++ *p &= ~mask; ++ __atomic_lock_release(); ++} ++ ++/* ++ * clear_bit() doesn't provide any barrier for the compiler. ++ */ ++#define smp_mb__before_clear_bit() barrier() ++#define smp_mb__after_clear_bit() barrier() ++ ++static inline void change_bit(int bit, volatile unsigned long *p) ++{ ++ unsigned long mask = 1UL << (bit & 31); ++ ++ p += bit >> 5; ++ ++ __atomic_lock_acquire(); ++ *p ^= mask; ++ __atomic_lock_release(); ++} ++ ++static inline int test_and_set_bit(int bit, volatile unsigned long *p) ++{ ++ unsigned int res; ++ unsigned long mask = 1UL << (bit & 31); ++ ++ p += bit >> 5; ++ ++ __atomic_lock_acquire(); ++ res = *p; ++ *p = res | mask; ++ __atomic_lock_release(); ++ ++ return res & mask; ++} ++ ++static inline int test_and_clear_bit(int bit, volatile unsigned long *p) ++{ ++ unsigned int res; ++ unsigned long mask = 1UL << (bit & 31); ++ ++ p += bit >> 5; ++ ++ __atomic_lock_acquire(); ++ res = *p; ++ *p = res & ~mask; ++ __atomic_lock_release(); ++ ++ return res & mask; ++} ++ ++static inline int test_and_change_bit(int bit, volatile unsigned long *p) ++{ ++ unsigned int res; ++ unsigned long mask = 1UL << (bit & 31); ++ ++ p += bit >> 5; ++ ++ __atomic_lock_acquire(); ++ res = *p; ++ *p = res ^ mask; ++ __atomic_lock_release(); ++ ++ return res & mask; ++} ++ ++#include <asm-generic/bitops/non-atomic.h> ++ ++/* ++ * This routine doesn't need to be atomic. ++ */ ++static inline int __constant_test_bit(int nr, const volatile unsigned long *addr) ++{ ++ return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0; ++} ++ ++static inline int __test_bit(int nr, const volatile unsigned long *addr) ++{ ++ int * a = (int *) addr; ++ int mask; ++ ++ a += nr >> 5; ++ mask = 1 << (nr & 0x1f); ++ return ((mask & *a) != 0); ++} ++ ++#define test_bit(nr,addr) (__builtin_constant_p(nr) ? __constant_test_bit((nr),(addr)) : __test_bit((nr),(addr))) ++ ++#include <asm-generic/bitops/find.h> ++#include <asm-generic/bitops/hweight.h> ++#include <asm-generic/bitops/lock.h> ++ ++#include <asm-generic/bitops/ext2-non-atomic.h> ++#include <asm-generic/bitops/ext2-atomic.h> ++#include <asm-generic/bitops/minix.h> ++ ++#endif /* __KERNEL__ */ ++ ++#include <asm-generic/bitops/fls.h> ++#include <asm-generic/bitops/fls64.h> ++ ++#endif /* _ASM_UBICOM32_BITOPS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/board.h +@@ -0,0 +1,34 @@ ++/* ++ * arch/ubicom32/include/asm/board.h ++ * Board init and revision definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_BOARD_H ++#define _ASM_UBICOM32_BOARD_H ++ ++extern const char *board_get_revision(void); ++extern void __init board_init(void); ++ ++#endif /* _ASM_UBICOM32_BOARD_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/bootargs.h +@@ -0,0 +1,34 @@ ++/* ++ * arch/ubicom32/include/asm/bootargs.h ++ * Kernel command line via the devtree API. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_BOOTARGS_H ++#define _ASM_UBICOM32_BOOTARGS_H ++ ++extern const char *bootargs_get_cmdline(void); ++extern void __init bootargs_init(void); ++ ++#endif /* _ASM_UBICOM32_BOOTARGS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/bootinfo.h +@@ -0,0 +1,34 @@ ++/* ++ * arch/ubicom32/include/asm/bootinfo.h ++ * Definitions of firmware boot parameters passed to the kernel. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_BOOTINFO_H ++#define _ASM_UBICOM32_BOOTINFO_H ++ ++/* Nothing for ubicom32 */ ++ ++#endif /* _ASM_UBICOM32_BOOTINFO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/bug.h +@@ -0,0 +1,53 @@ ++/* ++ * arch/ubicom32/include/asm/bug.h ++ * Generic bug.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_BUG_H ++#define _ASM_UBICOM32_BUG_H ++ ++#include <linux/kernel.h> ++#include <asm/thread.h> ++ ++#if defined(CONFIG_STOP_ON_BUG) ++/* ++ * __BUG_ON() ++ * Stall all threads to enable debugging. ++ */ ++static inline void __BUG_ON(unsigned long c) ++{ ++ if (unlikely(c)) { ++ THREAD_STALL; ++ } ++ return; ++} ++ ++#define BUG_ON(c) __BUG_ON((unsigned long)(c)) ++#define HAVE_ARCH_BUG_ON ++#endif ++ ++#include <asm-generic/bug.h> ++ ++#endif /* _ASM_UBICOM32_BUG_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/bugs.h +@@ -0,0 +1,44 @@ ++/* ++ * arch/ubicom32/include/asm/bugs.h ++ * Definition of check_bugs() for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1994 Linus Torvalds ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++/* ++ * This is included by init/main.c to check for architecture-dependent bugs. ++ * ++ * Needs: ++ * void check_bugs(void); ++ */ ++ ++#ifndef _ASM_UBICOM32_BUGS_H ++#define _ASM_UBICOM32_BUGS_H ++ ++static void check_bugs(void) ++{ ++} ++ ++#endif /* _ASM_UBICOM32_BUGS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/byteorder.h +@@ -0,0 +1,48 @@ ++/* ++ * arch/ubicom32/include/asm/byteorder.h ++ * Byte order swapping utility routines. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_BYTEORDER_H ++#define _ASM_UBICOM32_BYTEORDER_H ++ ++#include <linux/types.h> ++ ++#if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__) ++# define __BYTEORDER_HAS_U64__ ++# define __SWAB_64_THRU_32__ ++#endif ++ ++#if defined(IP7000) || defined(IP7000_REV2) ++ ++#define __arch__swab16 __builtin_ubicom32_swapb_2 ++#define __arch__swab32 __builtin_ubicom32_swapb_4 ++ ++#endif /* IP7000 */ ++ ++#include <linux/byteorder/big_endian.h> ++ ++#endif /* _ASM_UBICOM32_BYTEORDER_H */ ++ +--- /dev/null ++++ b/arch/ubicom32/include/asm/cachectl.h +@@ -0,0 +1,39 @@ ++/* ++ * arch/ubicom32/include/asm/cachectl.h ++ * Ubicom32 cache control definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_CACHECTL_H ++#define _ASM_UBICOM32_CACHECTL_H ++ ++#include <asm/ip5000.h> ++ ++/* ++ * mem_cache_control() ++ * Special cache control operation ++ */ ++extern void mem_cache_control(unsigned long cc, unsigned long begin_addr, unsigned long end_addr, unsigned long op); ++ ++#endif /* _ASM_UBICOM32_CACHECTL_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/cacheflush.h +@@ -0,0 +1,104 @@ ++/* ++ * arch/ubicom32/include/asm/cacheflush.h ++ * Cache flushing definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_CACHEFLUSH_H ++#define _ASM_UBICOM32_CACHEFLUSH_H ++ ++/* ++ * (C) Copyright 2000-2004, Greg Ungerer <gerg@snapgear.com> ++ */ ++#include <linux/mm.h> ++#include <asm/cachectl.h> ++#include <asm/ip5000.h> ++ ++#define flush_cache_all() __flush_cache_all() ++#define flush_cache_mm(mm) do { } while (0) ++#define flush_cache_dup_mm(mm) do { } while (0) ++#define flush_cache_range(vma, start, end) __flush_cache_all() ++#define flush_cache_page(vma, vmaddr) do { } while (0) ++#define flush_dcache_page(page) do { } while (0) ++#define flush_dcache_mmap_lock(mapping) do { } while (0) ++#define flush_dcache_mmap_unlock(mapping) do { } while (0) ++ ++#define flush_dcache_range(start, end) \ ++do { \ ++ /* Flush the data cache and invalidate the I cache. */ \ ++ local_irq_disable(); \ ++ mem_cache_control(DCCR_BASE, start, end, CCR_CTRL_FLUSH_ADDR); \ ++ mem_cache_control(ICCR_BASE, start, end, CCR_CTRL_INV_ADDR); \ ++ local_irq_enable(); \ ++} while (0) ++ ++#define flush_icache_range(start, end) \ ++do { \ ++ /* Flush the data cache and invalidate the I cache. */ \ ++ local_irq_disable(); \ ++ mem_cache_control(DCCR_BASE, start, end, CCR_CTRL_FLUSH_ADDR); \ ++ mem_cache_control(ICCR_BASE, start, end, CCR_CTRL_INV_ADDR); \ ++ local_irq_enable(); \ ++} while (0) ++ ++#define flush_icache_page(vma,pg) do { } while (0) ++#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) ++#define flush_cache_vmap(start, end) do { } while (0) ++#define flush_cache_vunmap(start, end) do { } while (0) ++ ++#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ ++ memcpy(dst, src, len) ++#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ ++ memcpy(dst, src, len) ++ ++/* ++ * Cache handling for IP5000 ++ */ ++extern inline void mem_cache_invalidate_all(unsigned long cc) ++{ ++ asm volatile ( ++ " bset "D(CCR_CTRL)"(%0), "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_RESET)" \n\t" ++ " nop \n\t" ++ " bclr "D(CCR_CTRL)"(%0), "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_RESET)" \n\t" ++ " pipe_flush 0 \n\t" ++ : ++ : "a"(cc) ++ : "cc" ++ ); ++} ++ ++static inline void __flush_cache_all(void) ++{ ++ /* ++ * Flush Icache ++ */ ++ mem_cache_invalidate_all(ICCR_BASE); ++ ++ /* ++ * Flush Dcache ++ */ ++ mem_cache_invalidate_all(DCCR_BASE); ++} ++ ++#endif /* _ASM_UBICOM32_CACHEFLUSH_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/cache.h +@@ -0,0 +1,40 @@ ++/* ++ * arch/ubicom32/include/asm/cache.h ++ * Cache line definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_CACHE_H ++#define _ASM_UBICOM32_CACHE_H ++ ++/* ++ * bytes per L1 cache line ++ */ ++#define L1_CACHE_SHIFT 5 ++#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) ++ ++#define __cacheline_aligned ++#define ____cacheline_aligned ++ ++#endif /* _ASM_UBICOM32_CACHE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/checksum.h +@@ -0,0 +1,149 @@ ++/* ++ * arch/ubicom32/include/asm/checksum.h ++ * Checksum utilities for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_CHECKSUM_H ++#define _ASM_UBICOM32_CHECKSUM_H ++ ++#include <linux/in6.h> ++ ++/* ++ * computes the checksum of a memory block at buff, length len, ++ * and adds in "sum" (32-bit) ++ * ++ * returns a 32-bit number suitable for feeding into itself ++ * or csum_tcpudp_magic ++ * ++ * this function must be called with even lengths, except ++ * for the last fragment, which may be odd ++ * ++ * it's best to have buff aligned on a 32-bit boundary ++ */ ++__wsum csum_partial(const void *buff, int len, __wsum sum); ++ ++/* ++ * the same as csum_partial, but copies from src while it ++ * checksums ++ * ++ * here even more important to align src and dst on a 32-bit (or even ++ * better 64-bit) boundary ++ */ ++ ++__wsum csum_partial_copy_nocheck(const void *src, void *dst, ++ int len, __wsum sum); ++ ++ ++/* ++ * the same as csum_partial_copy, but copies from user space. ++ * ++ * here even more important to align src and dst on a 32-bit (or even ++ * better 64-bit) boundary ++ */ ++ ++extern __wsum csum_partial_copy_from_user(const void __user *src, ++ void *dst, int len, __wsum sum, int *csum_err); ++ ++__sum16 ip_fast_csum(const void *iph, unsigned int ihl); ++ ++/* ++ * Fold a partial checksum ++ */ ++ ++static inline __sum16 csum_fold(__wsum sum) ++{ ++ asm volatile ( ++ " lsr.4 d15, %0, #16 \n\t" ++ " bfextu %0, %0, #16 \n\t" ++ " add.4 %0, d15, %0 \n\t" ++ " lsr.4 d15, %0, #16 \n\t" ++ " bfextu %0, %0, #16 \n\t" ++ " add.4 %0, d15, %0 \n\t" ++ : "=&d" (sum) ++ : "0"(sum) ++ : "d15" ++ ); ++ return (__force __sum16)~sum; ++} ++ ++ ++/* ++ * computes the checksum of the TCP/UDP pseudo-header ++ * returns a 16-bit checksum, already complemented ++ */ ++ ++static inline __wsum ++csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, ++ unsigned short proto, __wsum sum) ++{ ++ asm volatile ( ++ " add.4 %0, %2, %0 \n\t" ++ " addc %0, %3, %0 \n\t" ++ " addc %0, %4, %0 \n\t" ++ " addc %0, %5, %0 \n\t" ++ " addc %0, #0, %0 \n\t" ++ : "=&d" (sum) ++ : "0"(sum), "r" (saddr), "r" (daddr), "r" (len), "r"(proto) ++ ); ++ return sum; ++} ++ ++static inline __sum16 ++csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len, ++ unsigned short proto, __wsum sum) ++{ ++ return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); ++} ++ ++/* ++ * this routine is used for miscellaneous IP-like checksums, mainly ++ * in icmp.c ++ */ ++extern __sum16 ip_compute_csum(const void *buff, int len); ++ ++#define _HAVE_ARCH_IPV6_CSUM ++ ++static __inline__ __sum16 ++csum_ipv6_magic(const struct in6_addr *saddr, const struct in6_addr *daddr, ++ __u32 len, unsigned short proto, __wsum sum) ++{ ++ asm volatile ( ++ " add.4 %0, 0(%2), %0 \n\t" ++ " addc %0, 4(%2), %0 \n\t" ++ " addc %0, 8(%2), %0 \n\t" ++ " addc %0, 12(%2), %0 \n\t" ++ " addc %0, 0(%3), %0 \n\t" ++ " addc %0, 4(%3), %0 \n\t" ++ " addc %0, 8(%3), %0 \n\t" ++ " addc %0, 12(%3), %0 \n\t" ++ " addc %0, %4, %0 \n\t" ++ " addc %0, #0, %0 \n\t" ++ : "=&d" (sum) ++ : "0" (sum), "a" (saddr), "a" (daddr), "d" (len + proto) ++ ); ++ return csum_fold(sum); ++} ++ ++#endif /* _ASM_UBICOM32_CHECKSUM_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/cpu.h +@@ -0,0 +1,45 @@ ++/* ++ * arch/ubicom32/include/asm/cpu.h ++ * CPU definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2004-2005 ARM Ltd. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_CPU_H ++#define _ASM_UBICOM32_CPU_H ++ ++#include <linux/percpu.h> ++ ++struct cpuinfo_ubicom32 { ++ unsigned long tid; /* Hardware thread number */ ++ ++#ifdef CONFIG_SMP ++ volatile unsigned long ipi_pending; /* Bit map of operations to execute */ ++ unsigned long ipi_count; /* Number of IPI(s) taken on this cpu */ ++#endif ++}; ++ ++DECLARE_PER_CPU(struct cpuinfo_ubicom32, cpu_data); ++ ++#endif /* _ASM_UBICOM32_CPU_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/cputime.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/cputime.h ++ * Generic cputime.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_CPUTIME_H ++#define _ASM_UBICOM32_CPUTIME_H ++ ++#include <asm-generic/cputime.h> ++ ++#endif /* _ASM_UBICOM32_CPUTIME_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/current.h +@@ -0,0 +1,44 @@ ++/* ++ * arch/ubicom32/include/asm/current.h ++ * Definition of get_current() for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * (C) Copyright 2000, Lineo, David McCullough <davidm@uclinux.org> ++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_CURRENT_H ++#define _ASM_UBICOM32_CURRENT_H ++ ++#include <linux/thread_info.h> ++ ++struct task_struct; ++ ++static inline struct task_struct *get_current(void) ++{ ++ return(current_thread_info()->task); ++} ++ ++#define current get_current() ++ ++#endif /* _ASM_UBICOM32_CURRENT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/delay.h +@@ -0,0 +1,75 @@ ++/* ++ * arch/ubicom32/include/asm/delay.h ++ * Definition of delay routines for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_DELAY_H ++#define _ASM_UBICOM32_DELAY_H ++ ++#include <asm/param.h> ++#include <asm/ip5000.h> ++ ++static inline void __delay(unsigned long loops) ++{ ++ if (loops == 0) { ++ return; ++ } ++ ++ asm volatile ( ++ "1: add.4 %0, #-1, %0 \n\t" ++ " jmpne.t 1b \n\t" ++ : "+d" (loops) ++ ); ++} ++ ++/* ++ * Ubicom32 processor uses fixed 12MHz external OSC. ++ * So we use that as reference to count 12 cycles/us ++ */ ++ ++extern unsigned long loops_per_jiffy; ++ ++static inline void _udelay(unsigned long usecs) ++{ ++#if defined(CONFIG_UBICOM32_V4) || defined(CONFIG_UBICOM32_V3) ++ asm volatile ( ++ " add.4 d15, 0(%0), %1 \n\t" ++ " sub.4 #0, 0(%0), d15 \n\t" ++ " jmpmi.w.f .-4 \n\t" ++ : ++ : "a"(TIMER_BASE + TIMER_MPTVAL), "d"(usecs * (12000000/1000000)) ++ : "d15" ++ ); ++#else ++ BUG(); ++#endif ++} ++ ++/* ++ * Moved the udelay() function into library code, no longer inlined. ++ */ ++extern void udelay(unsigned long usecs); ++ ++#endif /* _ASM_UBICOM32_DELAY_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/device.h +@@ -0,0 +1,35 @@ ++/* ++ * arch/ubicom32/include/asm/device.h ++ * Generic device.h for Ubicom32 architecture. ++ * ++ * Used for arch specific extensions to struct device ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_DEVICE_H ++#define _ASM_UBICOM32_DEVICE_H ++ ++#include <asm-generic/device.h> ++ ++#endif /* _ASM_UBICOM32_DEVICE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/devtree.h +@@ -0,0 +1,52 @@ ++/* ++ * arch/ubicom32/include/asm/devtree.h ++ * Device Tree Header File (Shared between ultra and the Host OS) ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_DEVTREE_H ++#define _ASM_UBICOM32_DEVTREE_H ++ ++#define DEVTREE_MAX_NAME 32 ++#define DEVTREE_IRQ_NONE 0xff ++#define DEVTREE_IRQ_DONTCARE 0xff ++#define DEVTREE_NODE_MAGIC 0x10203040 ++ ++struct devtree_node { ++ struct devtree_node *next; ++ unsigned char sendirq; ++ unsigned char recvirq; ++ char name[DEVTREE_MAX_NAME]; ++ unsigned int magic; ++}; ++ ++extern struct devtree_node *devtree; ++extern struct devtree_node *devtree_find_by_irq(uint8_t sendirq, uint8_t recvirq); ++extern struct devtree_node *devtree_find_node(const char *str); ++extern struct devtree_node *devtree_find_next(struct devtree_node **cur); ++extern int devtree_irq(struct devtree_node *dn, unsigned char *sendirq, unsigned char *recvirq); ++extern void devtree_print(void); ++ ++#endif /* _ASM_UBICOM32_DEVTREE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/div64.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/div64.h ++ * Generic div64.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_DIV64_H ++#define _ASM_UBICOM32_DIV64_H ++ ++#include <asm-generic/div64.h> ++ ++#endif /* _ASM_UBICOM32_DIV64_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/dma.h +@@ -0,0 +1,34 @@ ++/* ++ * arch/ubicom32/include/asm/dma.h ++ * DMA definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_DMA_H ++#define _ASM_UBICOM32_DMA_H ++ ++/* Nothing so far */ ++#define MAX_DMA_ADDRESS 0x00 /* This is quite suspicious */ ++ ++#endif /* _ASM_UBICOM32_DMA_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/dma-mapping.h +@@ -0,0 +1,34 @@ ++/* ++ * arch/ubicom32/include/asm/dma-mapping.h ++ * Generic dma-mapping.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_DMA_MAPPING_H ++#define _ASM_UBICOM32_DMA_MAPPING_H ++ ++#include <linux/scatterlist.h> ++#include <asm-generic/dma-mapping.h> ++ ++#endif /* _ASM_UBICOM32_DMA_MAPPING_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/elf.h +@@ -0,0 +1,167 @@ ++/* ++ * arch/ubicom32/include/asm/elf.h ++ * Definitions for elf executable format for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_ELF_H ++#define _ASM_UBICOM32_ELF_H ++ ++/* ++ * ELF register definitions.. ++ */ ++ ++#include <asm/ptrace.h> ++#include <asm/user.h> ++ ++/* ++ * Processor specific flags for the ELF header e_flags field. ++ */ ++#define EF_UBICOM32_V3 0x00000001 /* -fmarch=ubicom32v3 */ ++#define EF_UBICOM32_V4 0x00000002 /* -fmarch=ubicom32v4 */ ++#define EF_UBICOM32_PIC 0x80000000 /* -fpic */ ++#define EF_UBICOM32_FDPIC 0x40000000 /* -mfdpic */ ++ ++/* ++ * Ubicom32 ELF relocation types ++ */ ++#define R_UBICOM32_NONE 0 ++#define R_UBICOM32_16 1 ++#define R_UBICOM32_32 2 ++#define R_UBICOM32_LO16 3 ++#define R_UBICOM32_HI16 4 ++#define R_UBICOM32_21_PCREL 5 ++#define R_UBICOM32_24_PCREL 6 ++#define R_UBICOM32_HI24 7 ++#define R_UBICOM32_LO7_S 8 ++#define R_UBICOM32_LO7_2_S 9 ++#define R_UBICOM32_LO7_4_S 10 ++#define R_UBICOM32_LO7_D 11 ++#define R_UBICOM32_LO7_2_D 12 ++#define R_UBICOM32_LO7_4_D 13 ++#define R_UBICOM32_32_HARVARD 14 ++#define R_UBICOM32_LO7_CALLI 15 ++#define R_UBICOM32_LO16_CALLI 16 ++#define R_UBICOM32_GOT_HI24 17 ++#define R_UBICOM32_GOT_LO7_S 18 ++#define R_UBICOM32_GOT_LO7_2_S 19 ++#define R_UBICOM32_GOT_LO7_4_S 20 ++#define R_UBICOM32_GOT_LO7_D 21 ++#define R_UBICOM32_GOT_LO7_2_D 22 ++#define R_UBICOM32_GOT_LO7_4_D 23 ++#define R_UBICOM32_FUNCDESC_GOT_HI24 24 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_S 25 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_2_S 26 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_4_S 27 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_D 28 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_2_D 29 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_4_D 30 ++#define R_UBICOM32_GOT_LO7_CALLI 31 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_CALLI 32 ++#define R_UBICOM32_FUNCDESC_VALUE 33 ++#define R_UBICOM32_FUNCDESC 34 ++#define R_UBICOM32_GOTOFFSET_LO 35 ++#define R_UBICOM32_GOTOFFSET_HI 36 ++#define R_UBICOM32_FUNCDESC_GOTOFFSET_LO 37 ++#define R_UBICOM32_FUNCDESC_GOTOFFSET_HI 38 ++#define R_UBICOM32_GNU_VTINHERIT 200 ++#define R_UBICOM32_GNU_VTENTRY 201 ++ ++typedef unsigned long elf_greg_t; ++ ++#define ELF_NGREG (sizeof(struct pt_regs) / sizeof(elf_greg_t)) ++typedef elf_greg_t elf_gregset_t[ELF_NGREG]; ++ ++typedef struct user_ubicom32fp_struct elf_fpregset_t; ++ ++/* ++ * This is used to ensure we don't load something for the wrong architecture. ++ */ ++#define elf_check_arch(x) ((x)->e_machine == EM_UBICOM32) ++ ++#define elf_check_fdpic(x) ((x)->e_flags & EF_UBICOM32_FDPIC) ++ ++#define elf_check_const_displacement(x) ((x)->e_flags & EF_UBICOM32_PIC) ++ ++/* ++ * These are used to set parameters in the core dumps. ++ */ ++#define ELF_CLASS ELFCLASS32 ++#define ELF_DATA ELFDATA2MSB ++#define ELF_ARCH EM_UBICOM32 ++ ++/* For SVR4/m68k the function pointer to be registered with `atexit' is ++ passed in %a1. Although my copy of the ABI has no such statement, it ++ is actually used on ASV. */ ++#define ELF_PLAT_INIT(_r, load_addr) _r->a1 = 0 ++ ++#define ELF_FDPIC_PLAT_INIT(_regs, _exec_map_addr, _interp_map_addr, \ ++ _dynamic_addr) \ ++ do { \ ++ _regs->dn[1] = _exec_map_addr; \ ++ _regs->dn[2] = _interp_map_addr; \ ++ _regs->dn[3] = _dynamic_addr; \ ++ _regs->an[1] = 0; /* dl_fini will be set by ldso */ \ ++ } while (0) ++ ++#define USE_ELF_CORE_DUMP ++#define ELF_EXEC_PAGESIZE 4096 ++ ++#ifdef __KERNEL__ ++#ifdef CONFIG_UBICOM32_V4 ++#define ELF_FDPIC_CORE_EFLAGS (EF_UBICOM32_FDPIC | EF_UBICOM32_V4) ++#elif defined CONFIG_UBICOM32_V3 ++#define ELF_FDPIC_CORE_EFLAGS (EF_UBICOM32_FDPIC | EF_UBICOM32_V3) ++#else ++#error Unknown/Unsupported ubicom32 architecture. ++#endif ++#endif ++ ++/* This is the location that an ET_DYN program is loaded if exec'ed. Typical ++ use of this is to invoke "./ld.so someprog" to test out a new version of ++ the loader. We need to make sure that it is out of the way of the program ++ that it will "exec", and that there is sufficient room for the brk. */ ++ ++#define ELF_ET_DYN_BASE 0xD0000000UL ++ ++/* ++ * For Ubicom32, the elf_gregset_t and struct pt_regs are the same size ++ * data structure so a copy is performed instead of providing the ++ * ELF_CORE_COPY_REGS macro. ++ */ ++ ++/* This yields a mask that user programs can use to figure out what ++ instruction set this cpu supports. */ ++ ++#define ELF_HWCAP (0) ++ ++/* This yields a string that ld.so will use to load implementation ++ specific libraries for optimization. This is more specific in ++ intent than poking at uname or /proc/cpuinfo. */ ++ ++#define ELF_PLATFORM (NULL) ++ ++#define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) ++ ++#endif /* _ASM_UBICOM32_ELF_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/elf.h~ +@@ -0,0 +1,165 @@ ++/* ++ * arch/ubicom32/include/asm/elf.h ++ * Definitions for elf executable format for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_ELF_H ++#define _ASM_UBICOM32_ELF_H ++ ++/* ++ * ELF register definitions.. ++ */ ++ ++#include <asm/ptrace.h> ++#include <asm/user.h> ++ ++/* ++ * Processor specific flags for the ELF header e_flags field. ++ */ ++#define EF_UBICOM32_V3 0x00000001 /* -fmarch=ubicom32v3 */ ++#define EF_UBICOM32_V4 0x00000002 /* -fmarch=ubicom32v4 */ ++#define EF_UBICOM32_PIC 0x80000000 /* -fpic */ ++#define EF_UBICOM32_FDPIC 0x40000000 /* -mfdpic */ ++ ++/* ++ * Ubicom32 ELF relocation types ++ */ ++#define R_UBICOM32_NONE 0 ++#define R_UBICOM32_16 1 ++#define R_UBICOM32_32 2 ++#define R_UBICOM32_LO16 3 ++#define R_UBICOM32_HI16 4 ++#define R_UBICOM32_21_PCREL 5 ++#define R_UBICOM32_24_PCREL 6 ++#define R_UBICOM32_HI24 7 ++#define R_UBICOM32_LO7_S 8 ++#define R_UBICOM32_LO7_2_S 9 ++#define R_UBICOM32_LO7_4_S 10 ++#define R_UBICOM32_LO7_D 11 ++#define R_UBICOM32_LO7_2_D 12 ++#define R_UBICOM32_LO7_4_D 13 ++#define R_UBICOM32_32_HARVARD 14 ++#define R_UBICOM32_LO7_CALLI 15 ++#define R_UBICOM32_LO16_CALLI 16 ++#define R_UBICOM32_GOT_HI24 17 ++#define R_UBICOM32_GOT_LO7_S 18 ++#define R_UBICOM32_GOT_LO7_2_S 19 ++#define R_UBICOM32_GOT_LO7_4_S 20 ++#define R_UBICOM32_GOT_LO7_D 21 ++#define R_UBICOM32_GOT_LO7_2_D 22 ++#define R_UBICOM32_GOT_LO7_4_D 23 ++#define R_UBICOM32_FUNCDESC_GOT_HI24 24 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_S 25 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_2_S 26 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_4_S 27 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_D 28 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_2_D 29 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_4_D 30 ++#define R_UBICOM32_GOT_LO7_CALLI 31 ++#define R_UBICOM32_FUNCDESC_GOT_LO7_CALLI 32 ++#define R_UBICOM32_FUNCDESC_VALUE 33 ++#define R_UBICOM32_FUNCDESC 34 ++#define R_UBICOM32_GOTOFFSET_LO 35 ++#define R_UBICOM32_GOTOFFSET_HI 36 ++#define R_UBICOM32_FUNCDESC_GOTOFFSET_LO 37 ++#define R_UBICOM32_FUNCDESC_GOTOFFSET_HI 38 ++#define R_UBICOM32_GNU_VTINHERIT 200 ++#define R_UBICOM32_GNU_VTENTRY 201 ++ ++typedef unsigned long elf_greg_t; ++ ++#define ELF_NGREG (sizeof(struct pt_regs) / sizeof(elf_greg_t)) ++typedef elf_greg_t elf_gregset_t[ELF_NGREG]; ++ ++typedef struct user_ubicom32fp_struct elf_fpregset_t; ++ ++/* ++ * This is used to ensure we don't load something for the wrong architecture. ++ */ ++#define elf_check_arch(x) ((x)->e_machine == EM_UBICOM32) ++ ++#define elf_check_fdpic(x) ((x)->e_flags & EF_UBICOM32_FDPIC) ++ ++#define elf_check_const_displacement(x) ((x)->e_flags & EF_UBICOM32_PIC) ++ ++/* ++ * These are used to set parameters in the core dumps. ++ */ ++#define ELF_CLASS ELFCLASS32 ++#define ELF_DATA ELFDATA2MSB ++#define ELF_ARCH EM_UBICOM32 ++ ++/* For SVR4/m68k the function pointer to be registered with `atexit' is ++ passed in %a1. Although my copy of the ABI has no such statement, it ++ is actually used on ASV. */ ++#define ELF_PLAT_INIT(_r, load_addr) _r->a1 = 0 ++ ++#define ELF_FDPIC_PLAT_INIT(_regs, _exec_map_addr, _interp_map_addr, \ ++ _dynamic_addr) \ ++ do { \ ++ _regs->dn[1] = _exec_map_addr; \ ++ _regs->dn[2] = _interp_map_addr; \ ++ _regs->dn[3] = _dynamic_addr; \ ++ _regs->an[1] = 0; /* dl_fini will be set by ldso */ \ ++ } while (0) ++ ++#define USE_ELF_CORE_DUMP ++#define ELF_EXEC_PAGESIZE 4096 ++ ++#ifdef CONFIG_UBICOM32_V4 ++#define ELF_FDPIC_CORE_EFLAGS (EF_UBICOM32_FDPIC | EF_UBICOM32_V4) ++#elif defined CONFIG_UBICOM32_V3 ++#define ELF_FDPIC_CORE_EFLAGS (EF_UBICOM32_FDPIC | EF_UBICOM32_V3) ++#else ++#error Unknown/Unsupported ubicom32 architecture. ++#endif ++ ++/* This is the location that an ET_DYN program is loaded if exec'ed. Typical ++ use of this is to invoke "./ld.so someprog" to test out a new version of ++ the loader. We need to make sure that it is out of the way of the program ++ that it will "exec", and that there is sufficient room for the brk. */ ++ ++#define ELF_ET_DYN_BASE 0xD0000000UL ++ ++/* ++ * For Ubicom32, the elf_gregset_t and struct pt_regs are the same size ++ * data structure so a copy is performed instead of providing the ++ * ELF_CORE_COPY_REGS macro. ++ */ ++ ++/* This yields a mask that user programs can use to figure out what ++ instruction set this cpu supports. */ ++ ++#define ELF_HWCAP (0) ++ ++/* This yields a string that ld.so will use to load implementation ++ specific libraries for optimization. This is more specific in ++ intent than poking at uname or /proc/cpuinfo. */ ++ ++#define ELF_PLATFORM (NULL) ++ ++#define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) ++ ++#endif /* _ASM_UBICOM32_ELF_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/emergency-restart.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/emergency-restart.h ++ * Generic emergency-restart.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_EMERGENCY_RESTART_H ++#define _ASM_UBICOM32_EMERGENCY_RESTART_H ++ ++#include <asm-generic/emergency-restart.h> ++ ++#endif /* _ASM_UBICOM32_EMERGENCY_RESTART_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/entry.h +@@ -0,0 +1,34 @@ ++/* ++ * arch/ubicom32/include/asm/entry.h ++ * Entry register/stack definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_ENTRY_H ++#define _ASM_UBICOM32_ENTRY_H ++ ++#include <asm/setup.h> ++#include <asm/page.h> ++ ++#endif /* _ASM_UBICOM32_ENTRY_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/errno.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/errno.h ++ * Generic errno.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_ERRNO_H ++#define _ASM_UBICOM32_ERRNO_H ++ ++#include <asm-generic/errno.h> ++ ++#endif /* _ASM_UBICOM32_ERRNO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/fb.h +@@ -0,0 +1,39 @@ ++/* ++ * arch/ubicom32/include/asm/fb.h ++ * Definition of fb_is_primary_device() for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_FB_H ++#define _ASM_UBICOM32_FB_H ++#include <linux/fb.h> ++ ++#define fb_pgprotect(...) do {} while (0) ++ ++static inline int fb_is_primary_device(struct fb_info *info) ++{ ++ return 0; ++} ++ ++#endif /* _ASM_UBICOM32_FB_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/fcntl.h +@@ -0,0 +1,38 @@ ++/* ++ * arch/ubicom32/include/asm/fcntl.h ++ * File control bit definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_FCNTL_H ++#define _ASM_UBICOM32_FCNTL_H ++ ++#define O_DIRECTORY 040000 /* must be a directory */ ++#define O_NOFOLLOW 0100000 /* don't follow links */ ++#define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ ++#define O_LARGEFILE 0400000 ++ ++#include <asm-generic/fcntl.h> ++ ++#endif /* _ASM_UBICOM32_FCNTL_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/flat.h +@@ -0,0 +1,73 @@ ++/* ++ * arch/ubicom32/include/asm/flat.h ++ * Definitions to support flat-format executables. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_FLAT_H ++#define _ASM_UBICOM32_FLAT_H ++ ++#define ARCH_FLAT_ALIGN 0x80 ++#define ARCH_FLAT_ALIGN_TEXT 1 ++ ++#define R_UBICOM32_32 2 ++#define R_UBICOM32_HI24 7 ++#define R_UBICOM32_LO7_S 8 ++#define R_UBICOM32_LO7_2_S 9 ++#define R_UBICOM32_LO7_4_S 10 ++#define R_UBICOM32_LO7_D 11 ++#define R_UBICOM32_LO7_2_D 12 ++#define R_UBICOM32_LO7_4_D 13 ++#define R_UBICOM32_LO7_CALLI 15 ++#define R_UBICOM32_LO16_CALLI 16 ++ ++extern void ubicom32_flat_put_addr_at_rp(unsigned long *rp, u32_t val, u32_t rval, unsigned long *p); ++extern unsigned long ubicom32_flat_get_addr_from_rp(unsigned long *rp, u32_t relval, u32_t flags, unsigned long *p); ++ ++#define flat_stack_align(sp) /* nothing needed */ ++#define flat_argvp_envp_on_stack() 1 ++#define flat_old_ram_flag(flags) (flags) ++#define flat_reloc_valid(reloc, size) ((reloc) <= (size)) ++#define flat_get_addr_from_rp(rp, relval, flags, p) (ubicom32_flat_get_addr_from_rp(rp, relval,flags, p)) ++#define flat_put_addr_at_rp(rp, val, relval) do {ubicom32_flat_put_addr_at_rp(rp, val, relval, &persistent);} while(0) ++#define flat_get_relocate_addr(rel) ((persistent) ? (persistent & 0x07ffffff) : (rel & 0x07ffffff)) ++ ++static inline int flat_set_persistent(unsigned int relval, unsigned long *p) ++{ ++ if (*p) { ++ return 0; ++ } else { ++ if ((relval >> 27) != R_UBICOM32_32) { ++ /* ++ * Something other than UBICOM32_32. The next entry has the relocation. ++ */ ++ *p = relval; ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++#endif /* _ASM_UBICOM32_FLAT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/fpu.h +@@ -0,0 +1,37 @@ ++/* ++ * arch/ubicom32/include/asm/fpu.h ++ * Floating point state definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_FPU_H ++#define _ASM_UBICOM32_FPU_H ++ ++/* ++ * MAX floating point unit state size (FSAVE/FRESTORE) ++ */ ++/* No FP unit present then... */ ++#define FPSTATESIZE (2) /* dummy size */ ++ ++#endif /* _ASM_UBICOM32_FPU_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/futex.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/futex.h ++ * Generic futex.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_FUTEX_H ++#define _ASM_UBICOM32_FUTEX_H ++ ++#include <asm-generic/futex.h> ++ ++#endif /* _ASM_UBICOM32_FUTEX_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/.gitignore +@@ -0,0 +1 @@ ++/ocm_size.h +--- /dev/null ++++ b/arch/ubicom32/include/asm/gpio.h +@@ -0,0 +1,451 @@ ++/* ++ * arch/ubicom32/include/asm/gpio.h ++ * Definitions for GPIO operations on Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_GPIO_H ++#define _ASM_UBICOM32_GPIO_H ++ ++#include <linux/compiler.h> ++#include <asm/irq.h> ++ ++#include <asm/ip5000.h> ++ ++#define ARCH_NR_GPIOS 512 ++#define MAX_UBICOM_ONCHIP_GPIO (9 * 32) ++ ++/* ++ * Macros for manipulating GPIO numbers ++ */ ++#define gpio_bit(gn) (1 << (gn & 0x1f)) ++#define gpio_bank(gn) (gn >> 5) ++ ++#define gpio_pin_index(gn) (gn & 0x1f) ++#define gpio_port_index(gn) (gn >> 5) ++ ++#define GPIO_RA_0 ((32 * 0) + 0) ++#define GPIO_RA_1 ((32 * 0) + 1) ++#define GPIO_RA_2 ((32 * 0) + 2) ++#define GPIO_RA_3 ((32 * 0) + 3) ++#define GPIO_RA_4 ((32 * 0) + 4) ++#define GPIO_RA_5 ((32 * 0) + 5) ++#define GPIO_RA_6 ((32 * 0) + 6) ++#define GPIO_RA_7 ((32 * 0) + 7) ++ ++#define GPIO_RB_0 ((32 * 1) + 0) ++#define GPIO_RB_1 ((32 * 1) + 1) ++#define GPIO_RB_2 ((32 * 1) + 2) ++#define GPIO_RB_3 ((32 * 1) + 3) ++#define GPIO_RB_4 ((32 * 1) + 4) ++#define GPIO_RB_5 ((32 * 1) + 5) ++#define GPIO_RB_6 ((32 * 1) + 6) ++#define GPIO_RB_7 ((32 * 1) + 7) ++#define GPIO_RB_8 ((32 * 1) + 8) ++#define GPIO_RB_9 ((32 * 1) + 9) ++#define GPIO_RB_10 ((32 * 1) + 10) ++#define GPIO_RB_11 ((32 * 1) + 11) ++#define GPIO_RB_12 ((32 * 1) + 12) ++#define GPIO_RB_13 ((32 * 1) + 13) ++#define GPIO_RB_14 ((32 * 1) + 14) ++#define GPIO_RB_15 ((32 * 1) + 15) ++#define GPIO_RB_16 ((32 * 1) + 16) ++#define GPIO_RB_17 ((32 * 1) + 17) ++#define GPIO_RB_18 ((32 * 1) + 18) ++#define GPIO_RB_19 ((32 * 1) + 19) ++ ++#define GPIO_RC_0 ((32 * 2) + 0) ++#define GPIO_RC_1 ((32 * 2) + 1) ++#define GPIO_RC_2 ((32 * 2) + 2) ++#define GPIO_RC_3 ((32 * 2) + 3) ++#define GPIO_RC_4 ((32 * 2) + 4) ++#define GPIO_RC_5 ((32 * 2) + 5) ++#define GPIO_RC_6 ((32 * 2) + 6) ++#define GPIO_RC_7 ((32 * 2) + 7) ++#define GPIO_RC_8 ((32 * 2) + 8) ++#define GPIO_RC_9 ((32 * 2) + 9) ++#define GPIO_RC_10 ((32 * 2) + 10) ++#define GPIO_RC_11 ((32 * 2) + 11) ++#define GPIO_RC_12 ((32 * 2) + 12) ++#define GPIO_RC_13 ((32 * 2) + 13) ++#define GPIO_RC_14 ((32 * 2) + 14) ++#define GPIO_RC_15 ((32 * 2) + 15) ++#define GPIO_RC_16 ((32 * 2) + 16) ++#define GPIO_RC_17 ((32 * 2) + 17) ++#define GPIO_RC_18 ((32 * 2) + 18) ++#define GPIO_RC_19 ((32 * 2) + 19) ++#define GPIO_RC_20 ((32 * 2) + 20) ++#define GPIO_RC_21 ((32 * 2) + 21) ++#define GPIO_RC_22 ((32 * 2) + 22) ++#define GPIO_RC_23 ((32 * 2) + 23) ++#define GPIO_RC_24 ((32 * 2) + 24) ++#define GPIO_RC_25 ((32 * 2) + 25) ++#define GPIO_RC_26 ((32 * 2) + 26) ++#define GPIO_RC_27 ((32 * 2) + 27) ++#define GPIO_RC_28 ((32 * 2) + 28) ++#define GPIO_RC_29 ((32 * 2) + 29) ++#define GPIO_RC_30 ((32 * 2) + 30) ++#define GPIO_RC_31 ((32 * 2) + 31) ++ ++#define GPIO_RD_0 ((32 * 3) + 0) ++#define GPIO_RD_1 ((32 * 3) + 1) ++#define GPIO_RD_2 ((32 * 3) + 2) ++#define GPIO_RD_3 ((32 * 3) + 3) ++#define GPIO_RD_4 ((32 * 3) + 4) ++#define GPIO_RD_5 ((32 * 3) + 5) ++#define GPIO_RD_6 ((32 * 3) + 6) ++#define GPIO_RD_7 ((32 * 3) + 7) ++#define GPIO_RD_8 ((32 * 3) + 8) ++#define GPIO_RD_9 ((32 * 3) + 9) ++#define GPIO_RD_10 ((32 * 3) + 10) ++#define GPIO_RD_11 ((32 * 3) + 11) ++ ++#define GPIO_RE_0 ((32 * 4) + 0) ++#define GPIO_RE_1 ((32 * 4) + 1) ++#define GPIO_RE_2 ((32 * 4) + 2) ++#define GPIO_RE_3 ((32 * 4) + 3) ++#define GPIO_RE_4 ((32 * 4) + 4) ++#define GPIO_RE_5 ((32 * 4) + 5) ++#define GPIO_RE_6 ((32 * 4) + 6) ++#define GPIO_RE_7 ((32 * 4) + 7) ++ ++#define GPIO_RF_0 ((32 * 5) + 0) ++#define GPIO_RF_1 ((32 * 5) + 1) ++#define GPIO_RF_2 ((32 * 5) + 2) ++#define GPIO_RF_3 ((32 * 5) + 3) ++#define GPIO_RF_4 ((32 * 5) + 4) ++#define GPIO_RF_5 ((32 * 5) + 5) ++#define GPIO_RF_6 ((32 * 5) + 6) ++#define GPIO_RF_7 ((32 * 5) + 7) ++#define GPIO_RF_8 ((32 * 5) + 8) ++#define GPIO_RF_9 ((32 * 5) + 9) ++#define GPIO_RF_10 ((32 * 5) + 10) ++#define GPIO_RF_11 ((32 * 5) + 11) ++#define GPIO_RF_12 ((32 * 5) + 12) ++#define GPIO_RF_13 ((32 * 5) + 13) ++#define GPIO_RF_14 ((32 * 5) + 14) ++#define GPIO_RF_15 ((32 * 5) + 15) ++ ++#define GPIO_RG_0 ((32 * 6) + 0) ++#define GPIO_RG_1 ((32 * 6) + 1) ++#define GPIO_RG_2 ((32 * 6) + 2) ++#define GPIO_RG_3 ((32 * 6) + 3) ++#define GPIO_RG_4 ((32 * 6) + 4) ++#define GPIO_RG_5 ((32 * 6) + 5) ++#define GPIO_RG_6 ((32 * 6) + 6) ++#define GPIO_RG_7 ((32 * 6) + 7) ++#define GPIO_RG_8 ((32 * 6) + 8) ++#define GPIO_RG_9 ((32 * 6) + 9) ++#define GPIO_RG_10 ((32 * 6) + 10) ++#define GPIO_RG_11 ((32 * 6) + 11) ++#define GPIO_RG_12 ((32 * 6) + 12) ++#define GPIO_RG_13 ((32 * 6) + 13) ++#define GPIO_RG_14 ((32 * 6) + 14) ++#define GPIO_RG_15 ((32 * 6) + 15) ++#define GPIO_RG_16 ((32 * 6) + 16) ++#define GPIO_RG_17 ((32 * 6) + 17) ++#define GPIO_RG_18 ((32 * 6) + 18) ++#define GPIO_RG_19 ((32 * 6) + 19) ++#define GPIO_RG_20 ((32 * 6) + 20) ++#define GPIO_RG_21 ((32 * 6) + 21) ++#define GPIO_RG_22 ((32 * 6) + 22) ++#define GPIO_RG_23 ((32 * 6) + 23) ++#define GPIO_RG_24 ((32 * 6) + 24) ++#define GPIO_RG_25 ((32 * 6) + 25) ++#define GPIO_RG_26 ((32 * 6) + 26) ++#define GPIO_RG_27 ((32 * 6) + 27) ++#define GPIO_RG_28 ((32 * 6) + 28) ++#define GPIO_RG_29 ((32 * 6) + 29) ++#define GPIO_RG_30 ((32 * 6) + 30) ++#define GPIO_RG_31 ((32 * 6) + 31) ++ ++#define GPIO_RH_0 ((32 * 7) + 0) ++#define GPIO_RH_1 ((32 * 7) + 1) ++#define GPIO_RH_2 ((32 * 7) + 2) ++#define GPIO_RH_3 ((32 * 7) + 3) ++#define GPIO_RH_4 ((32 * 7) + 4) ++#define GPIO_RH_5 ((32 * 7) + 5) ++#define GPIO_RH_6 ((32 * 7) + 6) ++#define GPIO_RH_7 ((32 * 7) + 7) ++#define GPIO_RH_8 ((32 * 7) + 8) ++#define GPIO_RH_9 ((32 * 7) + 9) ++ ++#define GPIO_RI_0 ((32 * 8) + 0) ++#define GPIO_RI_1 ((32 * 8) + 1) ++#define GPIO_RI_2 ((32 * 8) + 2) ++#define GPIO_RI_3 ((32 * 8) + 3) ++#define GPIO_RI_4 ((32 * 8) + 4) ++#define GPIO_RI_5 ((32 * 8) + 5) ++#define GPIO_RI_6 ((32 * 8) + 6) ++#define GPIO_RI_7 ((32 * 8) + 7) ++#define GPIO_RI_8 ((32 * 8) + 8) ++#define GPIO_RI_9 ((32 * 8) + 9) ++#define GPIO_RI_10 ((32 * 8) + 10) ++#define GPIO_RI_11 ((32 * 8) + 11) ++#define GPIO_RI_12 ((32 * 8) + 12) ++#define GPIO_RI_13 ((32 * 8) + 13) ++ ++/* ++ * The following section defines extra GPIO available to some boards. ++ * These GPIO are generally external to the processor (i.e. SPI/I2C ++ * expander chips). ++ * ++ * Note that these defines show all possible GPIO available, however, ++ * depending on the actual board configuration, some GPIO are not ++ * available for use. ++ */ ++#ifdef CONFIG_IP7500MEDIA ++/* ++ * U15 ++ */ ++#define IP7500MEDIA_U15_BASE (32 * 10) ++#define IP7500MEDIA_IO0 (IP7500MEDIA_U15_BASE + 0) ++#define IP7500MEDIA_IO1 (IP7500MEDIA_U15_BASE + 1) ++#define IP7500MEDIA_IO2 (IP7500MEDIA_U15_BASE + 2) ++#define IP7500MEDIA_IO3 (IP7500MEDIA_U15_BASE + 3) ++#define IP7500MEDIA_IO4 (IP7500MEDIA_U15_BASE + 4) ++#define IP7500MEDIA_IO5 (IP7500MEDIA_U15_BASE + 5) ++#define IP7500MEDIA_IO6 (IP7500MEDIA_U15_BASE + 6) ++#define IP7500MEDIA_IO7 (IP7500MEDIA_U15_BASE + 7) ++ ++/* ++ * U16 ++ */ ++#define IP7500MEDIA_U16_BASE (32 * 11) ++#define IP7500MEDIA_IO8 (IP7500MEDIA_U16_BASE + 0) ++#define IP7500MEDIA_IO9 (IP7500MEDIA_U16_BASE + 1) ++#define IP7500MEDIA_IO10 (IP7500MEDIA_U16_BASE + 2) ++#define IP7500MEDIA_IO11 (IP7500MEDIA_U16_BASE + 3) ++#define IP7500MEDIA_IO12 (IP7500MEDIA_U16_BASE + 4) ++#define IP7500MEDIA_IO13 (IP7500MEDIA_U16_BASE + 5) ++#define IP7500MEDIA_IO14 (IP7500MEDIA_U16_BASE + 6) ++#define IP7500MEDIA_IO15 (IP7500MEDIA_U16_BASE + 7) ++ ++/* ++ * U17 ++ */ ++#define IP7500MEDIA_U17_BASE (32 * 12) ++#define IP7500MEDIA_IO16 (IP7500MEDIA_U17_BASE + 0) ++#define IP7500MEDIA_IO17 (IP7500MEDIA_U17_BASE + 1) ++#define IP7500MEDIA_IO18 (IP7500MEDIA_U17_BASE + 2) ++#define IP7500MEDIA_IO19 (IP7500MEDIA_U17_BASE + 3) ++#define IP7500MEDIA_IO20 (IP7500MEDIA_U17_BASE + 4) ++#define IP7500MEDIA_IO21 (IP7500MEDIA_U17_BASE + 5) ++#define IP7500MEDIA_IO22 (IP7500MEDIA_U17_BASE + 6) ++#define IP7500MEDIA_IO23 (IP7500MEDIA_U17_BASE + 7) ++ ++/* ++ * U18 ++ */ ++#define IP7500MEDIA_U18_BASE (32 * 13) ++#define IP7500MEDIA_IO24 (IP7500MEDIA_U18_BASE + 0) ++#define IP7500MEDIA_IO25 (IP7500MEDIA_U18_BASE + 1) ++#define IP7500MEDIA_IO26 (IP7500MEDIA_U18_BASE + 2) ++#define IP7500MEDIA_IO27 (IP7500MEDIA_U18_BASE + 3) ++#define IP7500MEDIA_IO28 (IP7500MEDIA_U18_BASE + 4) ++#define IP7500MEDIA_IO29 (IP7500MEDIA_U18_BASE + 5) ++#define IP7500MEDIA_IO30 (IP7500MEDIA_U18_BASE + 6) ++#define IP7500MEDIA_IO31 (IP7500MEDIA_U18_BASE + 7) ++#endif ++ ++#ifdef CONFIG_IP7145DPF ++/* ++ * U48 ++ */ ++#define IP7145DPF_U48_BASE (32 * 10) ++#define IP7145DPF_IO0 (IP7145DPF_U48_BASE + 0) ++#define IP7145DPF_IO1 (IP7145DPF_U48_BASE + 1) ++#define IP7145DPF_IO2 (IP7145DPF_U48_BASE + 2) ++#define IP7145DPF_IO3 (IP7145DPF_U48_BASE + 3) ++#define IP7145DPF_IO4 (IP7145DPF_U48_BASE + 4) ++#define IP7145DPF_IO5 (IP7145DPF_U48_BASE + 5) ++#define IP7145DPF_IO6 (IP7145DPF_U48_BASE + 6) ++#define IP7145DPF_IO7 (IP7145DPF_U48_BASE + 7) ++ ++/* ++ * U72 ++ */ ++#define IP7145DPF_U72_BASE (32 * 11) ++#define IP7145DPF_IOB0 (IP7145DPF_U72_BASE + 0) ++#define IP7145DPF_IOB1 (IP7145DPF_U72_BASE + 1) ++#define IP7145DPF_IOB2 (IP7145DPF_U72_BASE + 2) ++#define IP7145DPF_IOB3 (IP7145DPF_U72_BASE + 3) ++#define IP7145DPF_IOB4 (IP7145DPF_U72_BASE + 4) ++#define IP7145DPF_IOB5 (IP7145DPF_U72_BASE + 5) ++#define IP7145DPF_IOB6 (IP7145DPF_U72_BASE + 6) ++#define IP7145DPF_IOB7 (IP7145DPF_U72_BASE + 7) ++#endif ++ ++#include <asm-generic/gpio.h> ++ ++/* ++ * The following macros bypass gpiolib to generate direct references ++ * to the port registers. These assume, minimally, that either ++ * gpio_direction_input() or gpio_direction_output() have already been ++ * called to setup the pin direction and to enable the pin function to ++ * be gpio. These macros generate the hardware port address based on ++ * the assumption that all ports are 32 bits wide (even though we know ++ * they are not). This is so we can efficiently turn pin numbers into ++ * port addresses without a lookup. ++ * ++ * These operations must be done in one instruction to prevent clobbering ++ * other thread's accesses to the same port. ++ */ ++#define UBICOM32_GPIO_ENABLE(pin) \ ++ do { \ ++ asm volatile ("or.4 (%[port]), (%[port]), %[mask]\n\t" \ ++ : \ ++ : [port] "a" (&UBICOM32_IO_PORT(IO_BASE + (gpio_bank(pin) << 12))->gpio_mask), \ ++ [mask] "d" (gpio_bit(pin)) \ ++ : "cc", "memory" \ ++ ); \ ++ } while (0); ++ ++#define UBICOM32_GPIO_DISABLE(pin) \ ++ do { \ ++ asm volatile ("and.4 (%[port]), (%[port]), %[mask]\n\t" \ ++ : \ ++ : [port] "a" (&UBICOM32_IO_PORT(IO_BASE + (gpio_bank(pin) << 12))->gpio_mask), \ ++ [mask] "d" (~gpio_bit(pin)) \ ++ : "cc", "memory" \ ++ ); \ ++ } while (0); ++ ++#define UBICOM32_GPIO_SET_PIN_INPUT(pin) \ ++ do { \ ++ asm volatile ("and.4 (%[port]), (%[port]), %[mask]\n\t" \ ++ : \ ++ : [port] "a" (&UBICOM32_IO_PORT(IO_BASE + (gpio_bank(pin) << 12))->gpio_ctl), \ ++ [mask] "d" (~gpio_bit(pin)) \ ++ : "cc", "memory" \ ++ ); \ ++ } while (0); ++ ++#define UBICOM32_GPIO_SET_PIN_OUTPUT(pin) \ ++ do { \ ++ asm volatile ("or.4 (%[port]), (%[port]), %[mask]\n\t" \ ++ : \ ++ : [port] "a" (&UBICOM32_IO_PORT(IO_BASE + (gpio_bank(pin) << 12))->gpio_ctl), \ ++ [mask] "d" (gpio_bit(pin)) \ ++ : "cc", "memory" \ ++ ); \ ++ } while (0); ++ ++#define UBICOM32_GPIO_SET_PIN_TOGGLE(pin) \ ++ do { \ ++ asm volatile ("xor.4 (%[port]), (%[port]), %[mask]\n\t" \ ++ : \ ++ : [port] "a" (&UBICOM32_IO_PORT(IO_BASE + (gpio_bank(pin) << 12))->gpio_out), \ ++ [mask] "d" (gpio_bit(pin)) \ ++ : "cc", "memory" \ ++ ); \ ++ } while (0); ++ ++#define UBICOM32_GPIO_SET_PIN_HIGH(pin) \ ++ do { \ ++ asm volatile ("or.4 (%[port]), (%[port]), %[mask]\n\t" \ ++ : \ ++ : [port] "a" (&UBICOM32_IO_PORT(IO_BASE + (gpio_bank(pin) << 12))->gpio_out), \ ++ [mask] "d" (gpio_bit(pin)) \ ++ : "cc", "memory" \ ++ ); \ ++ } while (0); ++ ++#define UBICOM32_GPIO_SET_PIN_LOW(pin) \ ++ do { \ ++ asm volatile ("and.4 (%[port]), (%[port]), %[mask]\n\t" \ ++ : \ ++ : [port] "a" (&UBICOM32_IO_PORT(IO_BASE + (gpio_bank(pin) << 12))->gpio_out), \ ++ [mask] "d" (~gpio_bit(pin)) \ ++ : "cc", "memory" \ ++ ); \ ++ } while (0); ++ ++#define UBICOM32_GPIO_SET_PIN(pin, val) \ ++ if ( val ) { \ ++ UBICOM32_GPIO_SET_PIN_HIGH(pin); \ ++ } else { \ ++ UBICOM32_GPIO_SET_PIN_LOW(pin); \ ++ } ++ ++#define UBICOM32_GPIO_GET_PIN(pin) \ ++ (0 != (UBICOM32_IO_PORT(IO_BASE + (gpio_bank(pin) << 12))->gpio_in \ ++ & gpio_bit(pin))) ++ ++ ++static inline int gpio_get_value(unsigned gpio) ++{ ++ if (gpio <= MAX_UBICOM_ONCHIP_GPIO) ++ return UBICOM32_GPIO_GET_PIN(gpio); ++ else ++ return __gpio_get_value(gpio); ++} ++ ++static inline void gpio_set_value(unsigned gpio, int value) ++{ ++ if (gpio <= MAX_UBICOM_ONCHIP_GPIO) ++ { ++ UBICOM32_GPIO_SET_PIN(gpio, value); ++ } ++ else ++ { ++ __gpio_set_value(gpio, value); ++ } ++} ++ ++static inline int gpio_cansleep(unsigned gpio) ++{ ++ return __gpio_cansleep(gpio); ++} ++ ++static inline int gpio_to_irq(unsigned gpio) ++{ ++#if defined(IP5000) || defined(IP5000_REV2) ++ if ((gpio >= GPIO_RA_4) && (gpio <= GPIO_RA_6)) ++ return 25; ++ else ++ return -ENXIO; ++ ++#elif defined(IP7000) || defined(IP7000_REV2) ++ if ((gpio >= GPIO_RA_4) && (gpio <= GPIO_RA_6)) ++ return 44 + (gpio - GPIO_RA_4); ++ else ++ return -ENXIO; ++ ++#else ++ return -ENXIO; ++ ++#endif ++} ++ ++static inline int irq_to_gpio(unsigned gpio) ++{ ++ return -ENXIO; ++} ++ ++extern struct ubicom32_io_port *ubi_gpio_get_port(unsigned gpio); ++ ++extern int __init ubi_gpio_init(void); ++ ++#endif /* _ASM_UBICOM32_GPIO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/hardirq.h +@@ -0,0 +1,55 @@ ++/* ++ * arch/ubicom32/include/asm/hardirq.h ++ * Definition of ack_bad_irq() for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1997, 98, 99, 2000, 01, 05 Ralf Baechle (ralf@linux-mips.org) ++ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. ++ * Copyright (C) 2001 MIPS Technologies, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_HARDIRQ_H ++#define _ASM_UBICOM32_HARDIRQ_H ++ ++#include <linux/threads.h> ++#include <linux/irq.h> ++ ++/* ++ * The hardirq mask has to be large enough to have space ++ * for potentially all IRQ sources in the system nesting ++ * on a single CPU. For Ubicom32, we have 64 IRQ sources. ++ */ ++#define HARDIRQ_BITS 6 ++#if (1 << HARDIRQ_BITS) < NR_IRQS ++# error HARDIRQ_BITS is too low! ++#endif ++ ++typedef struct { ++ unsigned int __softirq_pending; ++} ____cacheline_aligned irq_cpustat_t; ++ ++#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */ ++ ++extern void ack_bad_irq(unsigned int irq); ++ ++#endif /* _ASM_UBICOM32_HARDIRQ_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/hw_irq.h +@@ -0,0 +1,31 @@ ++/* ++ * arch/ubicom32/include/asm/hw_irq.h ++ * Ubicom32 architecture APIC support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_HW_IRQ_H ++#define _ASM_UBICOM32_HW_IRQ_H ++ ++#endif /* _ASM_UBICOM32_HW_IRQ_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ioctl.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/ioctl.h ++ * Generic ioctl.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_IOCTL_H ++#define _ASM_UBICOM32_IOCTL_H ++ ++#include <asm-generic/ioctl.h> ++ ++#endif /* _ASM_UBICOM32_IOCTL_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ioctls.h +@@ -0,0 +1,111 @@ ++/* ++ * arch/ubicom32/include/asm/ioctls.h ++ * Definitions of ioctls for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_IOCTLS_H ++#define _ASM_UBICOM32_IOCTLS_H ++ ++#include <asm/ioctl.h> ++ ++/* 0x54 is just a magic number to make these relatively unique ('T') */ ++ ++#define TCGETS 0x5401 ++#define TCSETS 0x5402 ++#define TCSETSW 0x5403 ++#define TCSETSF 0x5404 ++#define TCGETA 0x5405 ++#define TCSETA 0x5406 ++#define TCSETAW 0x5407 ++#define TCSETAF 0x5408 ++#define TCSBRK 0x5409 ++#define TCXONC 0x540A ++#define TCFLSH 0x540B ++#define TIOCEXCL 0x540C ++#define TIOCNXCL 0x540D ++#define TIOCSCTTY 0x540E ++#define TIOCGPGRP 0x540F ++#define TIOCSPGRP 0x5410 ++#define TIOCOUTQ 0x5411 ++#define TIOCSTI 0x5412 ++#define TIOCGWINSZ 0x5413 ++#define TIOCSWINSZ 0x5414 ++#define TIOCMGET 0x5415 ++#define TIOCMBIS 0x5416 ++#define TIOCMBIC 0x5417 ++#define TIOCMSET 0x5418 ++#define TIOCGSOFTCAR 0x5419 ++#define TIOCSSOFTCAR 0x541A ++#define FIONREAD 0x541B ++#define TIOCINQ FIONREAD ++#define TIOCLINUX 0x541C ++#define TIOCCONS 0x541D ++#define TIOCGSERIAL 0x541E ++#define TIOCSSERIAL 0x541F ++#define TIOCPKT 0x5420 ++#define FIONBIO 0x5421 ++#define TIOCNOTTY 0x5422 ++#define TIOCSETD 0x5423 ++#define TIOCGETD 0x5424 ++#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ ++#define TIOCSBRK 0x5427 /* BSD compatibility */ ++#define TIOCCBRK 0x5428 /* BSD compatibility */ ++#define TIOCGSID 0x5429 /* Return the session ID of FD */ ++#define TCGETS2 _IOR('T',0x2A, struct termios2) ++#define TCSETS2 _IOW('T',0x2B, struct termios2) ++#define TCSETSW2 _IOW('T',0x2C, struct termios2) ++#define TCSETSF2 _IOW('T',0x2D, struct termios2) ++#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ ++#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ ++ ++#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ ++#define FIOCLEX 0x5451 ++#define FIOASYNC 0x5452 ++#define TIOCSERCONFIG 0x5453 ++#define TIOCSERGWILD 0x5454 ++#define TIOCSERSWILD 0x5455 ++#define TIOCGLCKTRMIOS 0x5456 ++#define TIOCSLCKTRMIOS 0x5457 ++#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ ++#define TIOCSERGETLSR 0x5459 /* Get line status register */ ++#define TIOCSERGETMULTI 0x545A /* Get multiport config */ ++#define TIOCSERSETMULTI 0x545B /* Set multiport config */ ++ ++#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ ++#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ ++#define FIOQSIZE 0x545E ++ ++/* Used for packet mode */ ++#define TIOCPKT_DATA 0 ++#define TIOCPKT_FLUSHREAD 1 ++#define TIOCPKT_FLUSHWRITE 2 ++#define TIOCPKT_STOP 4 ++#define TIOCPKT_START 8 ++#define TIOCPKT_NOSTOP 16 ++#define TIOCPKT_DOSTOP 32 ++ ++#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ ++ ++#endif /* _ASM_UBICOM32_IOCTLS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/io.h +@@ -0,0 +1,313 @@ ++/* ++ * arch/ubicom32/include/asm/io.h ++ * I/O memory accessor functions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_IO_H ++#define _ASM_UBICOM32_IO_H ++ ++#ifdef __KERNEL__ ++#include <linux/string.h> ++#include <linux/compiler.h> ++ ++static inline unsigned short _swapw(volatile unsigned short v) ++{ ++ return ((v << 8) | (v >> 8)); ++} ++ ++static inline unsigned int _swapl(volatile unsigned long v) ++{ ++ return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24)); ++} ++ ++#ifndef CONFIG_PCI ++#define readb(addr) \ ++ ({ unsigned char __v = (*(volatile unsigned char *) (addr)); __v; }) ++#define readw(addr) \ ++ ({ unsigned short __v = (*(volatile unsigned short *) (addr)); __v; }) ++#define readl(addr) \ ++ ({ unsigned int __v = (*(volatile unsigned int *) (addr)); __v; }) ++ ++#define writeb(b,addr) (void)((*(volatile unsigned char *) (addr)) = (b)) ++#define writew(b,addr) (void)((*(volatile unsigned short *) (addr)) = (b)) ++#define writel(b,addr) (void)((*(volatile unsigned int *) (addr)) = (b)) ++#else /*CONFIG_PCI */ ++ ++#define PCI_CPU_REG_BASE (0x00000000UL) /* taking lower 2GB space */ ++#define PCI_DEV_REG_BASE (0x80000000UL) ++ ++#if PCI_CPU_REG_BASE > PCI_DEV_REG_BASE ++#define IS_PCI_ADDRESS(x) (((unsigned int)(x)&(PCI_CPU_REG_BASE)) == 0) ++#else ++#define IS_PCI_ADDRESS(x) ((unsigned int)(x)&(PCI_DEV_REG_BASE)) ++#endif ++ ++extern unsigned int ubi32_pci_read_u32(const volatile void __iomem *addr); ++extern unsigned short ubi32_pci_read_u16(const volatile void __iomem *addr); ++extern unsigned char ubi32_pci_read_u8(const volatile void __iomem *addr); ++extern void ubi32_pci_write_u32(unsigned int val, const volatile void __iomem *addr); ++extern void ubi32_pci_write_u16(unsigned short val, const volatile void __iomem *addr); ++extern void ubi32_pci_write_u8(unsigned char val, const volatile void __iomem *addr); ++ ++static inline unsigned char readb(const volatile void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ return ubi32_pci_read_u8(addr); ++ else ++ return (unsigned char)(*(volatile unsigned char *)addr); ++} ++static inline unsigned short readw(const volatile void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ return ubi32_pci_read_u16(addr); ++ else ++ return (unsigned short)(*(volatile unsigned short *)addr); ++} ++ ++static inline unsigned int readl(const volatile void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ return ubi32_pci_read_u32(addr); ++ else ++ return (unsigned int)(*(volatile unsigned int *)addr); ++} ++ ++static inline void writel(unsigned int val, volatile void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ ubi32_pci_write_u32(val, addr); ++ else ++ *(volatile unsigned int *)addr = val; ++} ++ ++static inline void writew(unsigned short val, volatile void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ ubi32_pci_write_u16(val, addr); ++ else ++ *(volatile unsigned short *)addr = val; ++} ++ ++static inline void writeb(unsigned char val, volatile void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ ubi32_pci_write_u8(val, addr); ++ else ++ *(volatile unsigned char *)addr = val; ++} ++#endif ++ ++#define readb_relaxed(addr) readb(addr) ++#define readw_relaxed(addr) readw(addr) ++#define readl_relaxed(addr) readl(addr) ++ ++ ++#define __raw_readb readb ++#define __raw_readw readw ++#define __raw_readl readl ++#define __raw_writeb writeb ++#define __raw_writew writew ++#define __raw_writel writel ++ ++static inline void io_outsb(unsigned int addr, const void *buf, int len) ++{ ++ volatile unsigned char *ap = (volatile unsigned char *) addr; ++ unsigned char *bp = (unsigned char *) buf; ++ while (len--) ++ *ap = *bp++; ++} ++ ++static inline void io_outsw(unsigned int addr, const void *buf, int len) ++{ ++ volatile unsigned short *ap = (volatile unsigned short *) addr; ++ unsigned short *bp = (unsigned short *) buf; ++ while (len--) ++ *ap = _swapw(*bp++); ++} ++ ++static inline void io_outsl(unsigned int addr, const void *buf, int len) ++{ ++ volatile unsigned int *ap = (volatile unsigned int *) addr; ++ unsigned int *bp = (unsigned int *) buf; ++ while (len--) ++ *ap = _swapl(*bp++); ++} ++ ++static inline void io_insb(unsigned int addr, void *buf, int len) ++{ ++ volatile unsigned char *ap = (volatile unsigned char *) addr; ++ unsigned char *bp = (unsigned char *) buf; ++ while (len--) ++ *bp++ = *ap; ++} ++ ++static inline void io_insw(unsigned int addr, void *buf, int len) ++{ ++ volatile unsigned short *ap = (volatile unsigned short *) addr; ++ unsigned short *bp = (unsigned short *) buf; ++ while (len--) ++ *bp++ = _swapw(*ap); ++} ++ ++static inline void io_insl(unsigned int addr, void *buf, int len) ++{ ++ volatile unsigned int *ap = (volatile unsigned int *) addr; ++ unsigned int *bp = (unsigned int *) buf; ++ while (len--) ++ *bp++ = _swapl(*ap); ++} ++ ++#define mmiowb() ++ ++/* ++ * make the short names macros so specific devices ++ * can override them as required ++ */ ++#ifndef CONFIG_PCI ++#define memset_io(a,b,c) memset((void *)(a),(b),(c)) ++#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) ++#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) ++#else ++extern void memcpy_fromio(void *to, const volatile void __iomem *from, unsigned len); ++extern void memcpy_toio(volatile void __iomem *to, const void *from, unsigned len); ++extern void memset_io(volatile void __iomem *addr, int val, size_t count); ++#endif ++ ++#define inb(addr) readb(addr) ++#define inw(addr) readw(addr) ++#define inl(addr) readl(addr) ++#define outb(x,addr) ((void) writeb(x,addr)) ++#define outw(x,addr) ((void) writew(x,addr)) ++#define outl(x,addr) ((void) writel(x,addr)) ++ ++#define inb_p(addr) inb(addr) ++#define inw_p(addr) inw(addr) ++#define inl_p(addr) inl(addr) ++#define outb_p(x,addr) outb(x,addr) ++#define outw_p(x,addr) outw(x,addr) ++#define outl_p(x,addr) outl(x,addr) ++ ++#define outsb(a,b,l) io_outsb(a,b,l) ++#define outsw(a,b,l) io_outsw(a,b,l) ++#define outsl(a,b,l) io_outsl(a,b,l) ++ ++#define insb(a,b,l) io_insb(a,b,l) ++#define insw(a,b,l) io_insw(a,b,l) ++#define insl(a,b,l) io_insl(a,b,l) ++ ++#ifndef CONFIG_PCI ++#define ioread8_rep(a,d,c) insb(a,d,c) ++#define ioread16_rep(a,d,c) insw(a,d,c) ++#define ioread32_rep(a,d,c) insl(a,d,c) ++#define iowrite8_rep(a,s,c) outsb(a,s,c) ++#define iowrite16_rep(a,s,c) outsw(a,s,c) ++#define iowrite32_rep(a,s,c) outsl(a,s,c) ++#else ++extern void ioread8_rep(void __iomem *port, void *buf, unsigned long count); ++extern void ioread16_rep(void __iomem *port, void *buf, unsigned long count); ++extern void ioread32_rep(void __iomem *port, void *buf, unsigned long count); ++extern void iowrite8_rep(void __iomem *port, const void *buf, unsigned long count); ++extern void iowrite16_rep(void __iomem *port, const void *buf, unsigned long count); ++extern void iowrite32_rep(void __iomem *port, const void *buf, unsigned long count); ++#endif ++ ++ ++#ifndef CONFIG_PCI ++#define ioread8(X) readb(X) ++#define ioread16(X) readw(X) ++#define ioread32(X) readl(X) ++#define iowrite8(val,X) writeb(val,X) ++#define iowrite16(val,X) writew(val,X) ++#define iowrite32(val,X) writel(val,X) ++#else /*CONFIG_PCI */ ++extern unsigned char ioread8(void __iomem *addr); ++extern unsigned short ioread16(void __iomem *addr); ++extern unsigned int ioread32(void __iomem *addr); ++extern void iowrite8(unsigned char val, void __iomem *addr); ++extern void iowrite16(unsigned short val, void __iomem *addr); ++extern void iowrite32(unsigned int val, void __iomem *addr); ++#endif /* CONFIG_PCI */ ++ ++#define IO_SPACE_LIMIT 0xffff ++ ++/* Values for nocacheflag and cmode */ ++#define IOMAP_FULL_CACHING 0 ++#define IOMAP_NOCACHE_SER 1 ++#define IOMAP_NOCACHE_NONSER 2 ++#define IOMAP_WRITETHROUGH 3 ++ ++extern void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag); ++extern void __iounmap(void *addr, unsigned long size); ++ ++static inline void *ioremap(unsigned long physaddr, unsigned long size) ++{ ++ return __ioremap(physaddr, size, IOMAP_NOCACHE_SER); ++} ++static inline void *ioremap_nocache(unsigned long physaddr, unsigned long size) ++{ ++ return __ioremap(physaddr, size, IOMAP_NOCACHE_SER); ++} ++static inline void *ioremap_writethrough(unsigned long physaddr, unsigned long size) ++{ ++ return __ioremap(physaddr, size, IOMAP_WRITETHROUGH); ++} ++static inline void *ioremap_fullcache(unsigned long physaddr, unsigned long size) ++{ ++ return __ioremap(physaddr, size, IOMAP_FULL_CACHING); ++} ++ ++extern void iounmap(void *addr); ++ ++#define ioport_map(port, nr) ((void __iomem*)(port)) ++#define ioport_unmap(addr) ++ ++ ++/* Pages to physical address... */ ++#define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) ++#define page_to_bus(page) ((page - mem_map) << PAGE_SHIFT) ++ ++/* ++ * Macros used for converting between virtual and physical mappings. ++ */ ++#define phys_to_virt(vaddr) ((void *) (vaddr)) ++#define virt_to_phys(vaddr) ((unsigned long) (vaddr)) ++ ++#define virt_to_bus virt_to_phys ++#define bus_to_virt phys_to_virt ++ ++/* ++ * Convert a physical pointer to a virtual kernel pointer for /dev/mem ++ * access ++ */ ++#define xlate_dev_mem_ptr(p) __va(p) ++ ++/* ++ * Convert a virtual cached pointer to an uncached pointer ++ */ ++#define xlate_dev_kmem_ptr(p) p ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _ASM_UBICOM32_IO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ip5000-asm.h +@@ -0,0 +1,156 @@ ++/* ++ * arch/ubicom32/include/asm/ip5000-asm.h ++ * Instruction macros for the IP5000. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_IP5000_ASM_H ++#define _ASM_UBICOM32_IP5000_ASM_H ++ ++#if !defined(__LINKER__) ++ ++#if defined(__ASSEMBLY__) ++.macro cycles quant ++.if (\quant) == 1 ++ nop ++.else ++.if (((\quant) + 3) / 8) > 0 ++.rept (((\quant) + 3) / 8) ++ jmpt.f .+4 ++.endr ++.endif ++.if ((((\quant) + 3) % 8) / 4) > 0 ++ jmpt.t .+4 ++.endif ++.endif ++.endm ++#else ++/* ++ * Same macro as above just in C inline asm ++ */ ++asm (" \n\ ++.macro cycles quant \n\ ++.if (\\quant) == 1 \n\ ++ nop \n\ ++.else \n\ ++.if (((\\quant) + 3) / 8) > 0 \n\ ++.rept (((\\quant) + 3) / 8) \n\ ++ jmpt.f .+4 \n\ ++.endr \n\ ++.endif \n\ ++.if ((((\\quant) + 3) % 8) / 4) > 0 \n\ ++ jmpt.t .+4 \n\ ++.endif \n\ ++.endif \n\ ++.endm \n\ ++"); ++#endif ++ ++ ++#if defined(__ASSEMBLY__) ++.macro pipe_flush cyc ++ cycles 11 - (\cyc) ++.endm ++#else ++/* ++ * Same macro as above just in C inline asm ++ */ ++asm (" \n\ ++.macro pipe_flush cyc \n\ ++ cycles 11 - (\\cyc) \n\ ++.endm \n\ ++"); ++ ++#endif ++ ++#if defined(__ASSEMBLY__) ++.macro setcsr_flush cyc ++ cycles 5 - (\cyc) ++.endm ++#else ++/* ++ * Same macro as above just in C inline asm ++ */ ++asm (" \n\ ++.macro setcsr_flush cyc \n\ ++ cycles 5 - (\\cyc) \n\ ++.endm \n\ ++"); ++#endif ++ ++/* ++ * Macros for prefetch (using miss-aligned memory write) ++ */ ++#if defined(__ASSEMBLY__) ++ ++.macro pre_fetch_macro thread_num, Ascratch, Aaddress length ++ bclr MT_TRAP_EN, MT_TRAP_EN, #(\thread_num) ++ bset \Ascratch, \Aaddress, #0 ; force a miss-aligned address ++ jmpt.t .+4 ; delay for both address setup and trap disable ++ move.4 (\Ascratch), #0 ++ .if (\length > 32) ++ move.4 32(\Ascratch), #0 ++ .endif ++ .if (\length > 64) ++ move.4 64(\Ascratch), #0 ++ .endif ++ .if (\length > 96) ++ move.4 96(\Ascratch), #0 ++ .endif ++ .if (\length > 128) ++ invalid_instruction ; maximum pre-fetch size is 4 cache lines ++ .endif ++ bset MT_TRAP_EN, MT_TRAP_EN, #(\thread_num) ++.endm ++ ++#else ++/* ++ * Same macro as above just in C inline asm ++ */ ++asm (" \n\ ++.macro pre_fetch_macro thread_num, Ascratch, Aaddress length \n\ ++ bclr MT_TRAP_EN, MT_TRAP_EN, #(\thread_num) \n\ ++ bset \\Ascratch, \\Aaddress, #0 ; force a miss-aligned address \n\ ++ jmpt.t .+4 ; delay for both address setup and trap disable \n\ ++ move.4 (\\Ascratch), #0 \n\ ++ .if (\\length > 32) \n\ ++ move.4 32(\\Ascratch), #0 \n\ ++ .endif \n\ ++ .if (\\length > 64) \n\ ++ move.4 64(\\Ascratch), #0 \n\ ++ .endif \n\ ++ .if (\\length > 96) \n\ ++ move.4 96(\\Ascratch), #0 \n\ ++ .endif \n\ ++ .if (\\length > 128) \n\ ++ invalid_instruction ; maximum pre-fetch size is 4 cache lines \n\ ++ .endif \n\ ++ bset MT_TRAP_EN, MT_TRAP_EN, #(\\thread_num) \n\ ++.endm \n\ ++"); ++#endif ++ ++#endif /* !defined(__LINKER__) */ ++#endif /* defined _ASM_UBICOM32_IP5000_ASM_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ip5000.h +@@ -0,0 +1,860 @@ ++/* ++ * arch/ubicom32/include/asm/ip5000.h ++ * Specific details for the Ubicom IP5000 processor. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_IP5000_H ++#define _ASM_UBICOM32_IP5000_H ++ ++/* ++ * Inline assembly define ++ */ ++#define S(arg) #arg ++#define D(arg) S(arg) ++ ++/* ++ * Assembler include file ++ */ ++#include <asm/ip5000-asm.h> ++ ++/* ++ * Timing ++ */ ++#define JMPT_PENALTY 3 ++#define JMPF_PENALTY 7 ++#define RET_PENALTY 7 ++ ++/* ++ * Threads ++ */ ++#if defined(IP5000) || defined(IP5000_REV2) ++#define THREAD_COUNT 10 ++#elif defined(IP7000) || defined(IP7000_REV2) ++#define THREAD_COUNT 12 ++#else ++#error "Unknown IP5K silicon" ++#endif ++ ++/* ++ * Arch ++ */ ++#if defined(IP5000) || defined(IP5000_REV2) ++#define UBICOM32_ARCH_VERSION 3 ++#elif defined(IP7000) || defined(IP7000_REV2) ++#define UBICOM32_ARCH_VERSION 4 ++#else ++#error "Unknown IP5K silicon" ++#endif ++ ++/* ++ * Memory Size ++ */ ++#define OCM_SECTOR_SIZE 0x00008000 /* 32K */ ++ ++#if defined(IP5000) || defined(IP5000_REV2) ++#define OCMSIZE 0x00030000 /* 192K on-chip RAM for both program and data */ ++#elif defined(IP7000) || defined(IP7000_REV2) ++#define OCMSIZE 0x0003C000 /* 240K on-chip RAM for both program and data */ ++#else ++#error "Unknown IP5K silicon" ++#endif ++ ++#define OCMSTART 0x3ffc0000 /* alias from 0x03000000 for easy jump to/from SDRAM */ ++#define OCMEND (OCMSTART + OCMSIZE) ++#define SDRAMSTART 0x40000000 ++#define FLASHSTART 0x60000000 ++ ++/* ++ * Registers ++ */ ++#define ROSR_INT (1 << 0) ++ ++/* Interrupts */ ++#define INT_CHIP(reg, bit) (((reg) << 5) | (bit)) ++#define INT_REG(interrupt) (((interrupt) >> 5) * 4) ++#define INT_SET(interrupt) 0x0114 + INT_REG(interrupt) ++#define INT_CLR(interrupt) 0x0124 + INT_REG(interrupt) ++#define INT_STAT(interrupt) 0x0104 + INT_REG(interrupt) ++#define INT_MASK(interrupt) 0x00C0 + INT_REG(interrupt) ++#define INT_BIT(interrupt) ((interrupt) & 0x1F) ++#define INT_BIT_MASK(interrupt) (1 << INT_BIT(interrupt)) ++ ++/* ++ * The LOCK_INT and THREAD_INT are used to wake up corresponding thread. They are sharing ++ * the same set of SW interrupt resource. ++ * ++ * LOCK_INT(n): One SW INT per NRT thread that can participate lock operation. ++ * The threads that can participate lock are application threads and DSR thread. ++ * (Lock locks - numbers are hard-coded in lock.h) ++ * THREAD_INT(n): One SW INT per HRT thread for wake up trigger. ++ */ ++#define LOCK_INT(thread) INT_CHIP(0, (thread)) ++#define THREAD_INT(thread) INT_CHIP(0, (thread)) ++ ++/* ++ * The SYSTEM_INT and DSR_INT are sharing the same set of SW interrupt resource. ++ * ++ * SYSTEM_INT(n): One SW INT per NRT threads (application threads) as system queue interrupt, ++ * and for DSR as self-trigger interrupt. ++ * (The application threads include at least thread 0) ++ * DSR_INT(n): One SW INT per HRT thread to request DSR service. ++ */ ++#define SYSTEM_INT(thread) INT_CHIP(0, THREAD_COUNT + (thread)) ++#define DSR_INT(thread) INT_CHIP(0, THREAD_COUNT + (thread)) ++ ++/* GLOBAL_CTRL */ ++#define GLOBAL_CTRL_TRAP_RST_EN (1 << 9) ++#define GLOBAL_CTRL_AERROR_RST_EN (1 << 8) ++#define GLOBAL_CTRL_MT_MIN_DELAY(x) ((x) << 3) ++#define GLOBAL_CTRL_HRT_BANK_SELECT (1 << 2) ++#define GLOBAL_CTRL_INT_EN (1 << 0) ++ ++/* ++ * HRT Tables ++ */ ++#define HRT_TABLE0_BASE 0x0800 ++#define HRT_TABLE1_BASE 0x0900 ++#define HRT_TABLE_SIZE 64 ++ ++/* ++ * Break Point Trap Register ++ */ ++#define ASYNCERROR_INT INT_CHIP(0, 31) ++#define BREAKPOINT_INT INT_CHIP(1, 31) ++ ++/* ++ * Port interrupts ++ * The non-existing FIFO INTs are mapped to INT2 for the ports. ++ */ ++#define IO_PORT_PTR_TO_NUM(port) (((port) & 0x0000ffff) >> 12) ++#define RX_FIFO_INT(port) \ ++ ((IO_PORT_PTR_TO_NUM(port) == 0) ? INT_CHIP(0, 25) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 1) ? INT_CHIP(0, 26) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 2) ? INT_CHIP(0, 29) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 3) ? INT_CHIP(1, 24) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 4) ? INT_CHIP(1, 27) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 5) ? INT_CHIP(1, 16) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 6) ? INT_CHIP(1, 19) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 7) ? INT_CHIP(1, 20) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 8) ? INT_CHIP(1, 21) : \ ++ INT_CHIP(1, 15)))))))))) ++#define TX_FIFO_INT(port) \ ++ ((IO_PORT_PTR_TO_NUM(port) == 0) ? INT_CHIP(0, 24) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 1) ? INT_CHIP(0, 27) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 2) ? INT_CHIP(0, 29) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 3) ? INT_CHIP(1, 25) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 4) ? INT_CHIP(1, 28) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 5) ? INT_CHIP(1, 17) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 6) ? INT_CHIP(1, 19) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 7) ? INT_CHIP(1, 20) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 8) ? INT_CHIP(1, 22) : \ ++ INT_CHIP(1, 15)))))))))) ++#define PORT_OTHER_INT(port) \ ++ ((IO_PORT_PTR_TO_NUM(port) == 0) ? INT_CHIP(0, 25) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 1) ? INT_CHIP(0, 28) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 2) ? INT_CHIP(0, 29) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 3) ? INT_CHIP(1, 26) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 4) ? INT_CHIP(1, 29) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 5) ? INT_CHIP(1, 18) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 6) ? INT_CHIP(1, 19) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 7) ? INT_CHIP(1, 20) : \ ++ ((IO_PORT_PTR_TO_NUM(port) == 8) ? INT_CHIP(1, 23) : \ ++ INT_CHIP(1, 15)))))))))) ++ ++/* ++ * On Chip Peripherals Base. ++ */ ++#define OCP_BASE 0x01000000 ++#define OCP_GENERAL 0x000 ++#define OCP_TIMERS 0x100 ++#define OCP_TRNG 0x200 /* True Random Number Generator Control Reigsters */ ++#define OCP_DEBUG 0x300 ++#define OCP_SECURITY 0x400 ++#define OCP_ICCR 0x500 /* I-Cache Control Registers */ ++#define OCP_DCCR 0x600 /* D-Cache Control Registers */ ++#define OCP_OCMC 0x700 /* On Chip Memory Control Registers */ ++#define OCP_STATISTICS 0x800 /* Statistics Counters */ ++#define OCP_MTEST 0x900 /* Memory Test Registers */ ++#define OCP_MCFG 0xa00 /* Memory Configuration Registers -- IP7000 only */ ++#define OCP_DEBUG_INST 0x000 /* Up to 16M */ ++ ++/* ++ * General Configuration Registers (PLL) ++ */ ++#define GENERAL_CFG_BASE (OCP_BASE + OCP_GENERAL) ++#define GEN_CLK_CORE_CFG 0x00 ++#define GEN_CLK_IO_CFG 0x04 ++#define GEN_CLK_DDR_CFG 0x08 ++#define GEN_CLK_DDRDS_CFG 0x0c ++#define GEN_CLK_SLIP_CLR 0x10 ++#define GEN_CLK_SLIP_START 0x14 ++#define GEN_CLK_SERDES_SEL 0x18 /* IP7000 only */ ++#define GEN_CLK_DDR_CFG2 0x1c /* IP7000 only */ ++#define GEN_DDR_CAL_CTRL 0x30 /* IP5000 only */ ++#define GEN_DDR_CAL_STAT 0x34 /* IP5000 only */ ++#define GEN_USB_DFT_CTRL 0x38 /* IP5000 only */ ++#define GEN_USB_DFT_STAT 0x3c /* IP5000 only */ ++#define GEN_USB_PHY_CFG 0x40 /* IP7000 only */ ++#define GEN_USB_PHY_TEST 0x44 /* IP7000 only */ ++#define GEN_USB_PHY_STAT 0x48 /* IP7000 only */ ++#define GEN_SW_RESET 0x80 ++#define GEN_RESET_REASON 0x84 ++#define GEN_BOND_CFG 0x88 ++#define GEN_IO_PU_CFG 0x8c ++#define GEN_MEM_RM_CFG 0x90 ++#define GEN_IO_CONFIG 0x94 ++ ++#define GEN_CLK_PLL_SECURITY_BIT_NO 31 ++#define GEN_CLK_PLL_SECURITY (1 << GEN_CLK_PLL_SECURITY_BIT_NO) ++#define GEN_CLK_PLL_ENSAT (1 << 30) ++#define GEN_CLK_PLL_FASTEN (1 << 29) ++#define GEN_CLK_PLL_NR(v) (((v) - 1) << 23) ++#define GEN_CLK_PLL_NF(v) (((v) - 1) << 11) ++#define GEN_CLK_PLL_OD(v) (((v) - 1) << 8) ++#define GEN_CLK_PLL_RESET (1 << 7) ++#define GEN_CLK_PLL_BYPASS (1 << 6) ++#define GEN_CLK_PLL_POWERDOWN (1 << 5) ++#define GEN_CLK_PLL_SELECT (1 << 4) ++ ++#define GEN_GET_CLK_PLL_NR(v) ((((v) >> 23) & 0x003f) + 1) ++#define GEN_GET_CLK_PLL_NF(v) ((((v) >> 11) & 0x0fff) + 1) ++#define GEN_GET_CLK_PLL_OD(v) ((((v) >> 8) & 0x7) + 1) ++ ++ ++#define RESET_FLAG_DST_MEM_ERROR (1 << 18) ++#define RESET_FLAG_SRC1_MEM_ERROR (1 << 17) ++#define RESET_FLAG_WRITE_ADDR (1 << 16) ++#define RESET_FLAG_DST_SYNC_ERROR (1 << 15) ++#define RESET_FLAG_SRC1_SYNC_ERROR (1 << 14) ++#define RESET_FLAG_DST_ALGN_ERROR (1 << 13) ++#define RESET_FLAG_SRC1_ALGN_ERROR (1 << 12) ++#define RESET_FLAG_DST_ADDR_ERROR (1 << 11) ++#define RESET_FLAG_SRC1_ADDR_ERROR (1 << 10) ++#define RESET_FLAG_ILLEGAL_INST (1 << 9) ++#define RESET_FLAG_INST_SYNC_ERROR (1 << 8) ++#define RESET_FLAG_INST_ADDR_ERROR (1 << 7) ++#define RESET_FLAG_DATA_PORT_ERROR (1 << 6) ++#define RESET_FLAG_INST_PORT_ERROR (1 << 5) ++#define RESET_FLAG_SW_RESET (1 << 4) ++#define RESET_FLAG_DEBUG (1 << 3) ++#define RESET_FLAG_WATCHDOG (1 << 2) ++#define RESET_FLAG_POWER_ON (1 << 1) ++#define RESET_FLAG_EXTERNAL (1 << 0) ++ ++/* ++ * Timer block ++ */ ++#define TIMER_BASE (OCP_BASE + OCP_TIMERS) ++#define TIMER_MPTVAL 0x00 ++#define TIMER_RTCOM 0x04 ++#define TIMER_TKEY 0x08 ++#define TIMER_WDCOM 0x0c ++#define TIMER_WDCFG 0x10 ++#define TIMER_SYSVAL 0x14 ++#define TIMER_SYSCOM(tmr) (0x18 + (tmr) * 4) ++#define TIMER_TRN_CFG 0x100 ++#define TIMER_TRN 0x104 ++ ++#define TIMER_COUNT 10 ++#define TIMER_INT(tmr) INT_CHIP(1, (tmr)) ++#define TIMER_TKEYVAL 0xa1b2c3d4 ++#define TIMER_WATCHDOG_DISABLE 0x4d3c2b1a ++#define TIMER_TRN_CFG_ENABLE_OSC 0x00000007 ++ ++#ifndef __ASSEMBLY__ ++/* ++ * ubicom32_io_timer ++ */ ++struct ubicom32_io_timer { ++ volatile u32_t mptval; ++ volatile u32_t rtcom; ++ volatile u32_t tkey; ++ volatile u32_t wdcom; ++ volatile u32_t wdcfg; ++ volatile u32_t sysval; ++ volatile u32_t syscom[TIMER_COUNT]; ++ volatile u32_t reserved[64 - 6 - TIMER_COUNT]; // skip all the way to OCP-TRNG section ++ volatile u32_t rsgcfg; ++ volatile u32_t trn; ++}; ++ ++#define UBICOM32_IO_TIMER ((struct ubicom32_io_timer *)TIMER_BASE) ++#endif ++ ++#define UBICOM32_VECTOR_TO_TIMER_INDEX(vector) (vector - TIMER_INT(0)) ++ ++/* ++ * OCP-Debug Module (Mailbox) ++ */ ++#define ISD_MAILBOX_BASE (OCP_BASE + OCP_DEBUG) ++#define ISD_MAILBOX_IN 0x00 ++#define ISD_MAILBOX_OUT 0x04 ++#define ISD_MAILBOX_STATUS 0x08 ++ ++#define ISD_MAILBOX_INT INT_CHIP(1, 30) ++ ++#define ISD_MAILBOX_STATUS_IN_FULL (1 << 31) ++#define ISD_MAILBOX_STATUS_IN_EMPTY (1 << 30) ++#define ISD_MAILBOX_STATUS_OUT_FULL (1 << 29) ++#define ISD_MAILBOX_STATUS_OUT_EMPTY (1 << 28) ++ ++/* ++ * OCP-Security ++ */ ++#define SECURITY_BASE (OCP_BASE + OCP_SECURITY) ++#define SECURITY_BASE_EFFECTIVE_ADDRESS (SECURITY_BASE >> 7) // To load the base address in a single instruction ++#define SECURITY_CTRL 0x00 ++#define SECURITY_CTRL_BYTE_OFFSET(x) ((x) << 16) ++#define SECURITY_CTRL_KEY_SIZE(x) ((x) << 8) ++#define SECURITY_CTRL_HASH_ALG_NONE (0 << 4) ++#define SECURITY_CTRL_HASH_ALG_MD5 (1 << 4) ++#define SECURITY_CTRL_HASH_ALG_SHA1 (2 << 4) ++#define SECURITY_CTRL_CBC (1 << 3) ++#define SECURITY_CTRL_CIPHER_ALG_AES (0 << 1) ++#define SECURITY_CTRL_CIPHER_ALG_NONE (1 << 1) ++#define SECURITY_CTRL_CIPHER_ALG_DES (2 << 1) ++#define SECURITY_CTRL_CIPHER_ALG_3DES (3 << 1) ++#define SECURITY_CTRL_ENCIPHER (1 << 0) ++#define SECURITY_CTRL_DECIPHER (0 << 0) ++#define SECURITY_STAT 0x04 ++#define SECURITY_STAT_BUSY (1 << 0) ++#define SECURITY_KEY_VALUE(x) (0x10 + (x) * 4) ++#define SECURITY_KEY_IN(x) (0x30 + (x) * 4) ++#define SECURITY_KEY_OUT(x) (0x50 + (x) * 4) ++#define SECURITY_KEY_HASH(x) (0x70 + (x) * 4) ++ ++/* ++ * OCP-ICCR ++ */ ++#define ICCR_BASE (OCP_BASE + OCP_ICCR) ++#define ICACHE_TOTAL_SIZE 16384 /* in bytes */ ++ ++/* ++ * OCP-DCCR ++ */ ++#define DCCR_BASE (OCP_BASE + OCP_DCCR) ++#if defined(IP5000) || defined(IP5000_REV2) ++#define DCACHE_TOTAL_SIZE 8192 /* in bytes */ ++#elif defined(IP7000) || defined(IP7000_REV2) ++#define DCACHE_TOTAL_SIZE 16384 /* in bytes */ ++#endif ++ ++#if defined(IP5000) || defined(IP5000_REV2) || defined(IP7000) || defined(IP7000_REV2) ++#define DCACHE_WRITE_QUEUE_LENGTH 6 ++#else ++#error "Unknown IP5K silicon" ++#endif ++ ++#define CACHE_LINE_SIZE 32 /* in bytes */ ++ ++#define CCR_ADDR 0x00 ++#define CCR_RDD 0x04 ++#define CCR_WRD 0x08 ++#define CCR_STAT 0x0c ++#define CCR_CTRL 0x10 ++ ++#define CCR_STAT_MCBE 0 ++#define CCR_STAT_WIDEL 1 /* D-cache only */ ++ ++#define CCR_CTRL_DONE 0 ++#define CCR_CTRL_RESET 2 ++#define CCR_CTRL_VALID 3 ++#define CCR_CTRL_RD_DATA (1 << 4) ++#define CCR_CTRL_RD_TAG (2 << 4) ++#define CCR_CTRL_WR_DATA (3 << 4) ++#define CCR_CTRL_WR_TAG (4 << 4) ++#define CCR_CTRL_INV_INDEX (5 << 4) ++#define CCR_CTRL_INV_ADDR (6 << 4) ++#define CCR_CTRL_FLUSH_INDEX (7 << 4) /* D-cache only */ ++#define CCR_CTRL_FLUSH_INV_INDEX (8 << 4) /* D-cache only */ ++#define CCR_CTRL_FLUSH_ADDR (9 << 4) /* D-cache only */ ++#define CCR_CTRL_FLUSH_INV_ADDR (10 << 4) /* D-cache only */ ++ ++/* ++ * OCP-OCMC ++ */ ++#define OCMC_BASE (OCP_BASE + OCP_OCMC) ++#define OCMC_BANK_MASK 0x00 ++#define OCMC_BIST_CNTL 0x04 /* IP5000 only */ ++#define OCMC_BIST_STAT 0x08 /* IP5000 only */ ++ ++#define OCMC_BANK_PROG(n) ((1<<(n))-1) ++ ++#define OCMC_BIST_WRCK (1 << 7) ++#define OCMC_BIST_RESET (1 << 5) ++#define OCMC_BIST_SMART (1 << 4) ++#define OCMC_BIST_RUN (1 << 3) ++#define OCMC_BIST_REPAIR (1 << 2) ++ ++#define OCMC_BIST_READY (1 << 3) ++#define OCMC_BIST_FAIL (1 << 2) ++ ++/* ++ * OCP-STATISTICS ++ */ ++#define STATISTICS_BASE (OCP_BASE + OCP_STATISTICS) ++#define STAT_COUNTER_CTRL(n) ((n)*8) ++#define STAT_COUNTER(n) ((n)*8 + 4) ++ ++#define STAT_EVENT_MP_INST 0 ++#define STAT_EVENT_OCM_ACCESS 4 ++#define STAT_EVENT_OCM_REQ 5 ++#define STAT_EVENT_IC_REQ_INVAL 13 ++#define STAT_EVENT_IC_MISS_INVAL 14 ++#define STAT_EVENT_IC_REQ_INVAL_NACK 15 ++#define STAT_EVENT_IC_REQ_VAL 16 ++#define STAT_EVENT_IC_MISS_VAL 17 ++#define STAT_EVENT_IC_REQ_VAL_NACK 18 ++#define STAT_EVENT_IC_MISS_Q 19 ++#define STAT_EVENT_DC_RD_REQ 20 ++#define STAT_EVENT_DC_RD_MISS 21 ++#define STAT_EVENT_DC_WR_REQ 22 ++#define STAT_EVENT_DC_WR_MISS 23 ++#define STAT_EVENT_DC_MISS_Q 24 ++#define STAT_EVENT_DC_WB_FULL 25 ++#define STAT_EVENT_DC_REQ_NACK 26 ++#define STAT_EVENT_DC_CORE_REQ 27 ++#define STAT_EVENT_DC_MISS 28 ++#define STAT_EVENT_DC_EVICT 29 ++#define STAT_EVENT_TRUE 30 ++#define STAT_EVENT_FALSE 31 ++ ++/* ++ * OCP_MTEST ++ */ ++#define MTEST_BASE (OCP_BASE + OCP_MTEST) ++#define MTEST_ADDR 0x00 ++#define MTEST_WR 0x04 ++#define MTEST_RD 0x08 ++#define MTEST_CTRL 0x0c ++ ++/* ++ * OCP_MCFG (IP7000 only) ++ */ ++#define MCFG_BASE (OCP_BASE + OCP_MCFG) ++#define MCFG_CTRL 0x00 ++#define MCFG_WCFG 0x04 ++#define MCFG_RCFG 0x08 ++ ++/* ++ * Port registers ++ */ ++#define IO_BASE 0x02000000 ++#define RA (IO_BASE + 0x00000000) ++#define RB (IO_BASE + 0x00001000) ++#define RC (IO_BASE + 0x00002000) ++#define RD (IO_BASE + 0x00003000) ++#define RE (IO_BASE + 0x00004000) ++#define RF (IO_BASE + 0x00005000) ++#define RG (IO_BASE + 0x00006000) ++#define RH (IO_BASE + 0x00007000) ++#define RI (IO_BASE + 0x00008000) ++#define RJ (IO_BASE + 0x00009000) ++#define RLATCH (IO_BASE + 0x00ff0000) // For latched output only ++#define IO_PORT_BR_OFFSET 0x00000800 ++ ++/* ++ * General I/O Register Map (per port) ++ */ ++#define IO_FUNC 0x00 ++#define IO_GPIO_CTL 0x04 ++#define IO_GPIO_OUT 0x08 ++#define IO_GPIO_IN 0x0C ++#define IO_INT_STATUS 0x10 ++#define IO_INT_MASK 0x14 ++#define IO_INT_SET 0x18 ++#define IO_INT_CLR 0x1C ++#define IO_TX_FIFO 0x20 ++#define IO_TX_FIFO_HI 0x24 ++#define IO_RX_FIFO 0x28 ++#define IO_RX_FIFO_HI 0x2c ++#define IO_CTL0 0x30 ++#define IO_CTL1 0x34 ++#define IO_CTL2 0x38 ++#define IO_STATUS0 0x3c ++#define IO_STATUS1 0x40 ++#define IO_STATUS2 0x44 ++#define IO_FIFO_WATER 0x48 ++#define IO_FIFO_LEVEL 0x4c ++#define IO_GPIO_MASK 0x50 ++ ++#define IO_FUNC_FUNCTION_RESET(func) ((1 << ((func) - 1)) << 4) /* Function 0 doesn't need reset */ ++#define IO_FUNC_RX_FIFO (1 << 3) ++#define IO_FUNC_SELECT(func) ((func) << 0) ++ ++/* ++ * External interrupt pins. ++ */ ++#define EXT_INT_IO_BIT(pin) ((pin) + 5) // Interrupt pin number -> I/O INT bit ++#define EXT_INT_RISING_EDGE(pin) (0x2 << (2*(pin) + 7)) ++#define EXT_INT_FALLING_EDGE(pin) (0x1 << (2*(pin) + 7)) ++ ++/* ++ * Flash ++ */ ++#define IO_XFL_BASE RA ++ ++#define IO_XFL_INT_START (1 << 16) ++#define IO_XFL_INT_ERR (1 << 8) ++#define IO_XFL_INT_DONE (1 << 0) ++ ++#define IO_XFL_CTL0_MASK (0xffe07fff) ++#define IO_XFL_CTL0_RD_CMD(cmd) (((cmd) & 0xff) << 24) ++#define IO_XFL_CTL0_RD_DUMMY(n) (((n) & 0x7) << 21) ++#define IO_XFL_CTL0_CLK_WIDTH(core_cycles) ((((core_cycles) + 1) & 0x7e) << 8) /* must be even number */ ++#define IO_XFL_CTL0_CE_WAIT(spi_cycles) (((spi_cycles) & 0x3f) << 2) ++#define IO_XFL_CTL0_MCB_LOCK (1 << 1) ++#define IO_XFL_CTL0_ENABLE (1 << 0) ++#define IO_XFL_CTL0_FAST_VALUE(div, wait) (IO_XFL_CTL0_RD_CMD(0xb) | IO_XFL_CTL0_RD_DUMMY(1) | IO_XFL_CTL0_CLK_WIDTH(div) | IO_XFL_CTL0_CE_WAIT(wait) | IO_XFL_CTL0_ENABLE) ++#define IO_XFL_CTL0_VALUE(div, wait) (IO_XFL_CTL0_RD_CMD(3) | IO_XFL_CTL0_CLK_WIDTH(div) | IO_XFL_CTL0_CE_WAIT(wait) | IO_XFL_CTL0_ENABLE) ++ ++#define IO_XFL_CTL1_MASK (0xc0003fff) ++#define IO_XFL_CTL1_FC_INST(inst) (((inst) & 0x3) << 30) ++#define IO_XFL_CTL1_FC_DATA(n) (((n) & 0x3ff) << 4) ++#define IO_XFL_CTL1_FC_DUMMY(n) (((n) & 0x7) << 1) ++#define IO_XFL_CTL1_FC_ADDR (1 << 0) ++ ++#define IO_XFL_CTL2_FC_CMD(cmd) (((cmd) & 0xff) << 24) ++#define IO_XFL_CTL2_FC_ADDR(addr) ((addr) & 0x00ffffff) /* Only up to 24 bits */ ++ ++#define IO_XFL_STATUS0_MCB_ACTIVE (1 << 0) ++#define IO_XFL_STATUS0_IOPCS_ACTIVE (1 << 1) ++ ++/* ++ * SDRAM ++ */ ++#define IO_SDRAM_DATA_BASE RG ++#define IO_SDRAM_CNTL_BASE RH ++ ++#define IO_SDRAM_CTRL0_EN_REF (1 << 0) ++ ++/* ++ * Port function code (common fucntion codes for all I/O ports) ++ */ ++#define IO_PORTX_FUNC_GPIO 0x00 ++#define IO_PORTX_FUNC_XFL 0x01 ++#define IO_PORTX_FUNC_PCI 0x01 ++#define IO_PORTX_FUNC_SERDES 0x01 ++#define IO_PORTX_FUNC_GMII 0x01 ++#define IO_PORTX_FUNC_DDR 0x01 ++#define IO_PORTX_FUNC_PCIX 0x01 ++#define IO_PORTX_FUNC_USB2_0 0x01 ++#define IO_PORTX_FUNC_GPIO_INT_CLK 0x02 ++#define IO_PORTX_FUNC_PLIO 0x02 ++#define IO_PORTX_FUNC_GPIO_INT 0x03 ++#define IO_PORTX_FUNC_MII 0x03 ++ ++/* ++ * Port 0 ++ */ ++#define IO_PORT0_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT0_FUNC_XFL_INT_CLK IO_PORTX_FUNC_XFL // Default mode after reset ++#define IO_PORT0_FUNC_GPIO_INT_CLK IO_PORTX_FUNC_GPIO_INT_CLK ++#define IO_PORT0_FUNC_GPIO_INT IO_PORTX_FUNC_GPIO_INT ++ ++/* ++ * Port 1 ++ */ ++#define IO_PORT1_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT1_FUNC_PCI IO_PORTX_FUNC_PCI // PCI control ++#define IO_PORT1_FUNC_MII IO_PORTX_FUNC_MII // port 4 MII extension ++ ++/* ++ * Port 2 ++ */ ++#define IO_PORT2_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT2_FUNC_PCI IO_PORTX_FUNC_PCI // PCI data I/O ++#define IO_PORT2_FUNC_PLIO IO_PORTX_FUNC_PLIO // Extended LM ++ ++/* ++ * Port 3 ++ */ ++#define IO_PORT3_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT3_FUNC_SERDES IO_PORTX_FUNC_SERDES ++#define IO_PORT3_FUNC_PLIO IO_PORTX_FUNC_PLIO ++ ++/* ++ * Port 4 ++ */ ++#define IO_PORT4_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT4_FUNC_SERDES IO_PORTX_FUNC_SERDES ++#define IO_PORT4_FUNC_PLIO IO_PORTX_FUNC_PLIO // Extended LM ++#define IO_PORT4_FUNC_MII IO_PORTX_FUNC_MII ++ ++/* ++ * Port 5 ++ */ ++#define IO_PORT5_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT5_FUNC_GMII IO_PORTX_FUNC_GMII ++ ++/* ++ * Port 6 ++ */ ++#define IO_PORT6_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT6_FUNC_DDR IO_PORTX_FUNC_DDR ++ ++/* ++ * Port 7 ++ */ ++#define IO_PORT7_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT7_FUNC_DDR IO_PORTX_FUNC_DDR ++ ++/* ++ * Port 8 ++ */ ++#define IO_PORT8_FUNC_GPIO IO_PORTX_FUNC_GPIO ++#define IO_PORT8_FUNC_PCIX IO_PORTX_FUNC_PCIX ++#define IO_PORT8_FUNC_PLIO IO_PORTX_FUNC_PLIO // Extended LM ++#define IO_PORT8_FUNC_MII IO_PORTX_FUNC_MII // port 4 MII extension ++ ++/* ++ * Port 9 ++ */ ++#define IO_PORT9_FUNC_USB2_0 IO_PORTX_FUNC_USB2_0 ++ ++/* ++ * FIFO ++ */ ++#define IO_PORTX_INT_FIFO_TX_RESET (1 << 31) ++#define IO_PORTX_INT_FIFO_RX_RESET (1 << 30) ++#define IO_PORTX_INT_FIFO_TX_UF (1 << 15) ++#define IO_PORTX_INT_FIFO_TX_WM (1 << 14) ++#define IO_PORTX_INT_FIFO_RX_OF (1 << 13) ++#define IO_PORTX_INT_FIFO_RX_WM (1 << 12) ++ ++#define IO_PORTX_FUNC_FIFO_TX_WM(n) ((n) << 16) ++#define IO_PORTX_FUNC_FIFO_RX_WM(n) ((n) << 0) ++ ++/* ++ * MII ++ */ ++#define IO_PORTX_INT_MII_TX_ERR_SEND (1 << 18) ++#define IO_PORTX_INT_MII_TX_HALT (1 << 17) ++#define IO_PORTX_INT_MII_TX_START (1 << 16) ++#define IO_PORTX_INT_MII_THRESHOLD (1 << 8) ++#define IO_PORTX_INT_MII_RX_EOP (1 << 7) ++#define IO_PORTX_INT_MII_RX_SFD (1 << 6) ++#define IO_PORTX_INT_MII_RX_ERR (1 << 5) ++#define IO_PORTX_INT_MII_TX_EOP (1 << 4) ++#define IO_PORTX_INT_MII_COL (1 << 3) ++#define IO_PORTX_INT_MII_CRS (1 << 2) ++#define IO_PORTX_INT_MII_ODD_NIB_ERR (1 << 1) ++#define IO_PORTX_INT_MII_FALSE_CARRIER (1 << 0) ++ ++/* ++ * SerDes ++ */ ++#define IO_PORTX_INT_SERDES_TXBUF_VALID (1 << 16) ++#define IO_PORTX_INT_SERDES_RXERR (1 << 7) ++#define IO_PORTX_INT_SERDES_RXEOP (1 << 6) ++#define IO_PORTX_INT_SERDES_SYND (1 << 5) ++#define IO_PORTX_INT_SERDES_TXBE (1 << 4) ++#define IO_PORTX_INT_SERDES_TXEOP (1 << 3) ++#define IO_PORTX_INT_SERDES_SXLP (1 << 2) ++#define IO_PORTX_INT_SERDES_RXBF (1 << 1) ++#define IO_PORTX_INT_SERDES_RXCRS (1 << 0) ++ ++#ifndef __ASSEMBLY__ ++struct ubicom32_io_port { ++ volatile u32_t function; ++ volatile u32_t gpio_ctl; ++ volatile u32_t gpio_out; ++ volatile u32_t gpio_in; ++ volatile u32_t int_status; ++ volatile u32_t int_mask; ++ volatile u32_t int_set; ++ volatile u32_t int_clr; ++ volatile u32_t tx_fifo; ++ volatile u32_t tx_fifo_hi; ++ volatile u32_t rx_fifo; ++ volatile u32_t rx_fifo_hi; ++ volatile u32_t ctl0; ++ volatile u32_t ctl1; ++ volatile u32_t ctl2; ++ volatile u32_t status0; ++ volatile u32_t status1; ++ volatile u32_t status2; ++ volatile u32_t fifo_watermark; ++ volatile u32_t fifo_level; ++ volatile u32_t gpio_mask; ++}; ++ ++#define UBICOM32_IO_PORT(port) ((struct ubicom32_io_port *)((port))) ++#endif ++ ++#ifndef __ASSEMBLY__ ++/* ++ * ubicom32_set_interrupt() ++ */ ++extern inline void ubicom32_set_interrupt(u8_t interrupt) ++{ ++ u32_t ibit = INT_BIT_MASK(interrupt); ++ ++ if (INT_REG(interrupt) == INT_REG(INT_CHIP(0, 0))) { ++ asm volatile ( ++ "move.4 "D(INT_SET(INT_CHIP(0, 0)))", %0\n\t" ++ : ++ : "r" (ibit) ++ ); ++ ++ return; ++ } ++ ++ asm volatile ( ++ "move.4 "D(INT_SET(INT_CHIP(1, 0)))", %0\n\t" ++ : ++ : "r" (ibit) ++ ); ++} ++ ++/* ++ * ubicom32_clear_interrupt() ++ */ ++extern inline void ubicom32_clear_interrupt(u8_t interrupt) ++{ ++ u32_t ibit = INT_BIT_MASK(interrupt); ++ ++ if (INT_REG(interrupt) == INT_REG(INT_CHIP(0, 0))) { ++ asm volatile ( ++ "move.4 "D(INT_CLR(INT_CHIP(0, 0)))", %0\n\t" ++ : ++ : "r" (ibit) ++ ); ++ ++ return; ++ } ++ ++ asm volatile ( ++ "move.4 "D(INT_CLR(INT_CHIP(1, 0)))", %0\n\t" ++ : ++ : "r" (ibit) ++ ); ++} ++ ++/* ++ * ubicom32_enable_interrupt() ++ */ ++extern inline void ubicom32_enable_interrupt(u8_t interrupt) ++{ ++ u32_t ibit = INT_BIT_MASK(interrupt); ++ ++ if (INT_REG(interrupt) == INT_REG(INT_CHIP(0, 0))) { ++ asm volatile ( ++ "or.4 "D(INT_MASK(INT_CHIP(0, 0)))", "D(INT_MASK(INT_CHIP(0, 0)))", %0\n\t" ++ : ++ : "d" (ibit) ++ ); ++ ++ return; ++ } ++ ++ asm volatile ( ++ "or.4 "D(INT_MASK(INT_CHIP(1, 0)))", "D(INT_MASK(INT_CHIP(1, 0)))", %0\n\t" ++ : ++ : "d" (ibit) ++ ); ++} ++ ++/* ++ * ubicom32_disable_interrupt() ++ */ ++extern inline void ubicom32_disable_interrupt(u8_t interrupt) ++{ ++ u32_t ibit = ~INT_BIT_MASK(interrupt); ++ ++ if (INT_REG(interrupt) == INT_REG(INT_CHIP(0, 0))) { ++ asm volatile ( ++ "and.4 "D(INT_MASK(INT_CHIP(0, 0)))", "D(INT_MASK(INT_CHIP(0, 0)))", %0\n\t" ++ : ++ : "d" (ibit) ++ ); ++ ++ return; ++ } ++ ++ asm volatile ( ++ "and.4 "D(INT_MASK(INT_CHIP(1, 0)))", "D(INT_MASK(INT_CHIP(1, 0)))", %0\n\t" ++ : ++ : "d" (ibit) ++ ); ++} ++ ++/* ++ * ubicom32_enable_global_interrupts() ++ */ ++extern inline void ubicom32_enable_global_interrupts(void) ++{ ++ asm volatile( ++ "bset GLOBAL_CTRL, GLOBAL_CTRL, #%bit("D(GLOBAL_CTRL_INT_EN)")" ++ ); ++} ++ ++/* ++ * ubicom32_disable_global_interrupts() ++ */ ++extern inline void ubicom32_disable_global_interrupts(void) ++{ ++ asm volatile( ++ "bclr GLOBAL_CTRL, GLOBAL_CTRL, #%bit("D(GLOBAL_CTRL_INT_EN)")" ++ ); ++} ++ ++/* ++ * ubicom32_get_reset_reason() ++ */ ++extern inline u32_t ubicom32_get_reset_reason(void) ++{ ++ return *(u32_t *)(GENERAL_CFG_BASE + GEN_RESET_REASON); ++} ++ ++/* ++ * ubicom32_read_reg() ++ */ ++extern inline u32_t ubicom32_read_reg(volatile void *reg) ++{ ++ u32_t v; ++ asm volatile ( ++ "move.4 %[dest], %[src] \n\t" ++ : [dest] "=r" (v) ++ : [src] "m" (*(u32_t *)reg) ++ ); ++ return v; ++} ++ ++/* ++ * ubicom32_write_reg() ++ */ ++extern inline void ubicom32_write_reg(volatile void *reg, u32_t v) ++{ ++ asm volatile ( ++ "move.4 %[dest], %[src] \n\t" ++ : ++ : [src] "r" (v), [dest] "m" (*(u32_t *)reg) ++ ); ++} ++ ++#endif /* __ASSEMBLY__ */ ++#endif /* _ASM_UBICOM32_IP5000_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ipcbuf.h +@@ -0,0 +1,55 @@ ++/* ++ * arch/ubicom32/include/asm/ipcbuf.h ++ * Definition of ipc64_perm struct for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_IPCBUF_H ++#define _ASM_UBICOM32_IPCBUF_H ++ ++/* ++ * The user_ipc_perm structure for m68k architecture. ++ * Note extra padding because this structure is passed back and forth ++ * between kernel and user space. ++ * ++ * Pad space is left for: ++ * - 32-bit mode_t and seq ++ * - 2 miscellaneous 32-bit values ++ */ ++struct ipc64_perm ++{ ++ __kernel_key_t key; ++ __kernel_uid32_t uid; ++ __kernel_gid32_t gid; ++ __kernel_uid32_t cuid; ++ __kernel_gid32_t cgid; ++ __kernel_mode_t mode; ++ unsigned short __pad1; ++ unsigned short seq; ++ unsigned short __pad2; ++ unsigned long __unused1; ++ unsigned long __unused2; ++}; ++ ++#endif /* _ASM_UBICOM32_IPCBUF_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/irqflags.h +@@ -0,0 +1,94 @@ ++/* ++ * arch/ubicom32/include/asm/irqflags.h ++ * Raw implementation of local IRQ functions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_IRQFLAGS_H ++#define _ASM_UBICOM32_IRQFLAGS_H ++ ++#include <linux/thread_info.h> ++#include <asm/ubicom32-common.h> ++#include <asm/smp.h> ++#include <asm/ldsr.h> ++ ++#if defined(CONFIG_PREEMPT) ++#error Not supported by Ubicom32 irq handling, yet! ++#endif ++ ++/* ++ * raw_local_irq_enable() ++ * Enable interrupts for this thread. ++ */ ++static inline void raw_local_irq_enable(void) ++{ ++ ldsr_local_irq_enable(); ++} ++ ++/* ++ * raw_local_irq_disable() ++ * Disable interrupts for this thread. ++ */ ++static inline void raw_local_irq_disable(void) ++{ ++ ldsr_local_irq_disable(); ++} ++ ++/* ++ * raw_local_save_flags() ++ * Get the current IRQ state. ++ */ ++#define raw_local_save_flags(flags) \ ++do { \ ++ (flags) = ldsr_local_irq_is_disabled(); \ ++} while (0) ++ ++/* ++ * raw_local_irq_save() ++ * Save the current interrupt state and disable interrupts. ++ */ ++#define raw_local_irq_save(flags) \ ++do { \ ++ (flags) = ldsr_local_irq_save(); \ ++} while (0) ++ ++/* ++ * raw_local_irq_restore() ++ * Restore the IRQ state back to flags. ++ */ ++static inline void raw_local_irq_restore(unsigned long flags) ++{ ++ ldsr_local_irq_restore(flags); ++} ++ ++/* ++ * raw_irqs_disabled_flags() ++ * Return true if the flags indicate that IRQ(s) are disabled. ++ */ ++static inline int raw_irqs_disabled_flags(unsigned long flags) ++{ ++ return (flags); ++} ++ ++#endif /* _ASM_UBICOM32_IRQFLAGS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/irq.h +@@ -0,0 +1,45 @@ ++/* ++ * arch/ubicom32/include/asm/irq.h ++ * IRQ definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_IRQ_H ++#define _ASM_UBICOM32_IRQ_H ++ ++#include <asm/irqflags.h> ++ ++/* ++ * We setup the IRQS to cover the full range of interrupt registers in ++ * processor. ++ */ ++#define NR_IRQS 64 ++ ++#define irq_canonicalize(irq) (irq) ++ ++extern int irq_soft_alloc(unsigned int *soft); ++extern void ack_bad_irq(unsigned int irq); ++extern void do_IRQ(int irq, struct pt_regs *fp); ++ ++#endif /* _ASM_UBICOM32_IRQ_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/irq_regs.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/irq_regs.h ++ * Generic irq_regs.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_IRQ_REGS_H ++#define _ASM_UBICOM32_IRQ_REGS_H ++ ++#include <asm-generic/irq_regs.h> ++ ++#endif /* _ASM_UBICOM32_IRQ_REGS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/Kbuild +@@ -0,0 +1 @@ ++include include/asm-generic/Kbuild.asm +--- /dev/null ++++ b/arch/ubicom32/include/asm/kdebug.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/kdebug.h ++ * Generic kdebug.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_KDEBUG_H ++#define _ASM_UBICOM32_KDEBUG_H ++ ++#include <asm-generic/kdebug.h> ++ ++#endif /* _ASM_UBICOM32_KDEBUG_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/kmap_types.h +@@ -0,0 +1,48 @@ ++/* ++ * arch/ubicom32/include/asm/kmap_types.h ++ * Definition of km_type's for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_KMAP_TYPES_H ++#define _ASM_UBICOM32_KMAP_TYPES_H ++ ++enum km_type { ++ KM_BOUNCE_READ, ++ KM_SKB_SUNRPC_DATA, ++ KM_SKB_DATA_SOFTIRQ, ++ KM_USER0, ++ KM_USER1, ++ KM_BIO_SRC_IRQ, ++ KM_BIO_DST_IRQ, ++ KM_PTE0, ++ KM_PTE1, ++ KM_IRQ0, ++ KM_IRQ1, ++ KM_SOFTIRQ0, ++ KM_SOFTIRQ1, ++ KM_TYPE_NR ++}; ++ ++#endif /* _ASM_UBICOM32_KMAP_TYPES_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ldsr.h +@@ -0,0 +1,186 @@ ++/* ++ * arch/ubicom32/include/asm/ldsr.h ++ * Ubicom32 LDSR interface definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_LDSR_H ++#define _ASM_UBICOM32_LDSR_H ++ ++#include <asm/ubicom32-common.h> ++#include <asm/types.h> ++#include <asm/thread.h> ++ ++extern unsigned int ldsr_soft_irq_mask; ++ ++/* ++ * ldsr_local_irq_is_disabled() ++ * Test if interrupts are disabled for this thread? ++ */ ++static inline int ldsr_local_irq_is_disabled(void) ++{ ++ int ret; ++ thread_t self = thread_get_self(); ++ unsigned int mask = (1 << self); ++ ++ asm volatile ( ++ " and.4 %0, scratchpad1, %1 \n\t" ++ : "=r" (ret) ++ : "d" (mask) ++ : "cc" ++ ); ++ ++ /* ++ * We return a simple 1 == disabled, 0 == enabled ++ * losing which tid this is for, because Linux ++ * can restore interrupts on a different thread. ++ */ ++ return ret >> self; ++} ++ ++/* ++ * ldsr_local_irq_save() ++ * Get the current interrupt state and disable interrupts. ++ */ ++static inline unsigned int ldsr_local_irq_save(void) ++{ ++ int ret; ++ thread_t self = thread_get_self(); ++ unsigned int mask = (1 << self); ++ ++ /* ++ * Ensure the compiler can not optimize out the code ++ * (volatile) and that it does not "cache" values around ++ * the interrupt state change (memory). This ensures ++ * that interrupt changes are treated as a critical ++ * section. ++ */ ++ asm volatile ( ++ " and.4 %0, scratchpad1, %1 \n\t" ++ " or.4 scratchpad1, scratchpad1, %1 \n\t" ++ : "=&r" (ret) ++ : "d" (mask) ++ : "cc", "memory" ++ ); ++ ++ /* ++ * We return a simple 1 == disabled, 0 == enabled ++ * losing which tid this is for, because Linux ++ * can restore interrupts on a different thread. ++ */ ++ return ret >> self; ++} ++ ++/* ++ * ldsr_local_irq_restore() ++ * Restore this cpu's interrupt enable/disable state. ++ * ++ * Note: flags is either 0 or 1. ++ */ ++static inline void ldsr_local_irq_restore(unsigned int flags) ++{ ++ unsigned int temp; ++ thread_t self = thread_get_self(); ++ unsigned int mask = (1 << self); ++ flags = (flags << self); ++ ++ /* ++ * Ensure the compiler can not optimize out the code ++ * (volatile) and that it does not "cache" values around ++ * the interrupt state change (memory). This ensures ++ * that interrupt changes are treated as a critical ++ * section. ++ * ++ * Atomic change to our bit in scratchpad1 without ++ * causing any temporary glitch in the value and ++ * without effecting other values. Also this uses ++ * no branches so no penalties. ++ */ ++ asm volatile ( ++ " xor.4 %0, scratchpad1, %1 \n\t" ++ " and.4 %0, %2, %0 \n\t" ++ " xor.4 scratchpad1, scratchpad1, %0 \n\t" ++ " move.4 int_set0, %3 \n\t" ++ : "=&d"(temp) ++ : "d"(flags), "r"(mask), "r"(ldsr_soft_irq_mask) ++ : "cc", "memory" ++ ); ++} ++ ++/* ++ * ldsr_local_irq_disable_interrupt() ++ * Disable ints for this thread. ++ */ ++static inline void ldsr_local_irq_disable(void) ++{ ++ unsigned int mask = (1 << thread_get_self()); ++ ++ /* ++ * Ensure the compiler can not optimize out the code ++ * (volatile) and that it does not "cache" values around ++ * the interrupt state change (memory). This ensures ++ * that interrupt changes are treated as a critical ++ * section. ++ */ ++ asm volatile ( ++ " or.4 scratchpad1, scratchpad1, %0 \n\t" ++ : ++ : "d" (mask) ++ : "cc", "memory" ++ ); ++} ++ ++/* ++ * ldsr_local_irq_enable_interrupt ++ * Enable ints for this thread. ++ */ ++static inline void ldsr_local_irq_enable(void) ++{ ++ unsigned int mask = (1 << thread_get_self()); ++ ++ /* ++ * Ensure the compiler can not optimize out the code ++ * (volatile) and that it does not "cache" values around ++ * the interrupt state change (memory). This ensures ++ * that interrupt changes are treated as a critical ++ * section. ++ */ ++ asm volatile ( ++ " and.4 scratchpad1, scratchpad1, %0 \n\t" ++ " move.4 int_set0, %1 \n\t" ++ : ++ : "d" (~mask), "r" (ldsr_soft_irq_mask) ++ : "cc", "memory" ++ ); ++} ++ ++extern void ldsr_init(void); ++extern void ldsr_set_trap_irq(unsigned int irq); ++extern void ldsr_mask_vector(unsigned int vector); ++extern void ldsr_unmask_vector(unsigned int vector); ++extern void ldsr_enable_vector(unsigned int vector); ++extern void ldsr_disable_vector(unsigned int vector); ++extern thread_t ldsr_get_threadid(void); ++ ++#endif /* _ASM_UBICOM32_LDSR_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/linkage.h +@@ -0,0 +1,34 @@ ++/* ++ * arch/ubicom32/include/asm/linkage.h ++ * Definition of Ubicom32 architecture specific linkage types. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_LINKAGE_H ++#define _ASM_UBICOM32_LINKAGE_H ++ ++#define __ocm_text __section(.ocm_text) ++#define __ocm_data __section(.ocm_data) ++ ++#endif /* _ASM_UBICOM32_LINKAGE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/local.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/local.h ++ * Generic local.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_LOCAL_H ++#define _ASM_UBICOM32_LOCAL_H ++ ++#include <asm-generic/local.h> ++ ++#endif /* _ASM_UBICOM32_LOCAL_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/machdep.h +@@ -0,0 +1,43 @@ ++/* ++ * arch/ubicom32/include/asm/machdep.h ++ * Machine dependent utility routines. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_MACHDEP_H ++#define _ASM_UBICOM32_MACHDEP_H ++ ++#include <linux/interrupt.h> ++ ++/* Hardware clock functions */ ++extern unsigned long hw_timer_offset(void); ++ ++/* machine dependent power off functions */ ++extern void (*mach_reset)(void); ++extern void (*mach_halt)(void); ++extern void (*mach_power_off)(void); ++ ++extern void config_BSP(char *command, int len); ++ ++#endif /* _ASM_UBICOM32_MACHDEP_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/mc146818rtc.h +@@ -0,0 +1,36 @@ ++/* ++ * arch/ubicom32/include/asm/mc146818rtc.h ++ * Generic mc146818rtc.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++/* ++ * Machine dependent access functions for RTC registers. ++ */ ++#ifndef _ASM_UBICOM32_MC146818RTC_H ++#define _ASM_UBICOM32_MC146818RTC_H ++ ++/* empty include file to satisfy the include in genrtc.c/ide-geometry.c */ ++ ++#endif /* _ASM_UBICOM32_MC146818RTC_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/mman.h +@@ -0,0 +1,44 @@ ++/* ++ * arch/ubicom32/include/asm/mman.h ++ * Memory mapping definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_MMAN_H ++#define _ASM_UBICOM32_MMAN_H ++ ++#include <asm-generic/mman.h> ++ ++#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ ++#define MAP_DENYWRITE 0x0800 /* ETXTBSY */ ++#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ ++#define MAP_LOCKED 0x2000 /* pages are locked */ ++#define MAP_NORESERVE 0x4000 /* don't check for reservations */ ++#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ ++#define MAP_NONBLOCK 0x10000 /* do not block on IO */ ++ ++#define MCL_CURRENT 1 /* lock all current mappings */ ++#define MCL_FUTURE 2 /* lock all future mappings */ ++ ++#endif /* _ASM_UBICOM32_MMAN_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/mmu_context.h +@@ -0,0 +1,60 @@ ++/* ++ * arch/ubicom32/include/asm/mmu_context.h ++ * MMU context definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2004, Microtronix Datacom Ltd., All rights reserved. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_MMU_CONTEXT_H ++#define _ASM_UBICOM32_MMU_CONTEXT_H ++ ++#include <asm/setup.h> ++#include <asm/page.h> ++#include <asm/pgalloc.h> ++ ++static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) ++{ ++} ++ ++extern inline int ++init_new_context(struct task_struct *tsk, struct mm_struct *mm) ++{ ++ // mm->context = virt_to_phys(mm->pgd); ++ return(0); ++} ++ ++#define destroy_context(mm) do { } while(0) ++ ++static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) ++{ ++} ++ ++#define deactivate_mm(tsk,mm) do { } while (0) ++ ++extern inline void activate_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm) ++{ ++} ++ ++#endif /* _ASM_UBICOM32_MMU_CONTEXT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/mmu.h +@@ -0,0 +1,41 @@ ++/* ++ * arch/ubicom32/include/asm/mmu.h ++ * Definition of mm_context_t struct for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2002, David McCullough <davidm@snapgear.com> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_MMU_H ++#define _ASM_UBICOM32_MMU_H ++ ++typedef struct { ++ struct vm_list_struct *vmlist; ++ unsigned long end_brk; ++#ifdef CONFIG_BINFMT_ELF_FDPIC ++ unsigned long exec_fdpic_loadmap; ++ unsigned long interp_fdpic_loadmap; ++#endif ++} mm_context_t; ++ ++#endif /* _ASM_UBICOM32_MMU_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/module.h +@@ -0,0 +1,44 @@ ++/* ++ * arch/ubicom32/include/asm/module.h ++ * Ubicom32 architecture specific module definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_MODULE_H ++#define _ASM_UBICOM32_MODULE_H ++ ++struct mod_arch_specific { ++ void *ocm_inst; ++ int ocm_inst_size; ++}; ++ ++#define Elf_Shdr Elf32_Shdr ++#define Elf_Sym Elf32_Sym ++#define Elf_Ehdr Elf32_Ehdr ++ ++#define ARCH_PROC_MODULES_EXTRA(m,mod) \ ++ seq_printf(m, " OCM(%d bytes @ 0x%p)", \ ++ (mod)->arch.ocm_inst_size, (mod)->arch.ocm_inst) ++ ++#endif /* _ASM_UBICOM32_MODULE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/msgbuf.h +@@ -0,0 +1,58 @@ ++/* ++ * arch/ubicom32/include/asm/msgbuf.h ++ * Definition of msqid64_ds struct for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_MSGBUF_H ++#define _ASM_UBICOM32_MSGBUF_H ++ ++/* ++ * The msqid64_ds structure for ubicom32 architecture. ++ * Note extra padding because this structure is passed back and forth ++ * between kernel and user space. ++ * ++ * Pad space is left for: ++ * - 64-bit time_t to solve y2038 problem ++ * - 2 miscellaneous 32-bit values ++ */ ++ ++struct msqid64_ds { ++ struct ipc64_perm msg_perm; ++ __kernel_time_t msg_stime; /* last msgsnd time */ ++ unsigned long __unused1; ++ __kernel_time_t msg_rtime; /* last msgrcv time */ ++ unsigned long __unused2; ++ __kernel_time_t msg_ctime; /* last change time */ ++ unsigned long __unused3; ++ unsigned long msg_cbytes; /* current number of bytes on queue */ ++ unsigned long msg_qnum; /* number of messages in queue */ ++ unsigned long msg_qbytes; /* max number of bytes on queue */ ++ __kernel_pid_t msg_lspid; /* pid of last msgsnd */ ++ __kernel_pid_t msg_lrpid; /* last receive pid */ ++ unsigned long __unused4; ++ unsigned long __unused5; ++}; ++ ++#endif /* _ASM_UBICOM32_MSGBUF_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/mutex.h +@@ -0,0 +1,41 @@ ++/* ++ * arch/ubicom32/include/asm/mutex.h ++ * Generic mutex.h for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++/* ++ * Pull in the generic implementation for the mutex fastpath. ++ * ++ * TODO: implement optimized primitives instead, or leave the generic ++ * implementation in place, or pick the atomic_xchg() based generic ++ * implementation. (see asm-generic/mutex-xchg.h for details) ++ */ ++ ++#ifndef _ASM_UBICOM32_MUTEX_H ++#define _ASM_UBICOM32_MUTEX_H ++ ++#include <asm-generic/mutex-dec.h> ++ ++#endif /* _ASM_UBICOM32_MUTEX_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/namei.h +@@ -0,0 +1,38 @@ ++/* ++ * arch/ubicom32/include/asm/namei.h ++ * Definition of __emul_prefix() for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_NAMEI_H ++#define _ASM_UBICOM32_NAMEI_H ++ ++/* This dummy routine maybe changed to something useful ++ * for /usr/gnemul/ emulation stuff. ++ * Look at asm-sparc/namei.h for details. ++ */ ++ ++#define __emul_prefix() NULL ++ ++#endif /* _ASM_UBICOM32_NAMEI_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ocm-alloc.h +@@ -0,0 +1,36 @@ ++/* ++ * arch/ubicom32/include/asm/ocm-alloc.h ++ * Ubicom32 architecture specific ocm definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_OCM_ALLOC_H ++#define _ASM_UBICOM32_OCM_ALLOC_H ++ ++ ++extern void *ocm_inst_alloc(size_t size, pid_t pid); ++extern int ocm_free(const void *ptr); ++extern int ocm_inst_free(const void *ptr); ++ ++#endif /* _ASM_UBICOM32_OCM_ALLOC_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ocm_size.h +@@ -0,0 +1,2 @@ ++#define APP_OCM_CODE_SIZE (0x3ffc2e00-0x3ffc0000) ++#define APP_OCM_DATA_SIZE (0x3ffd3500-0x3ffc8000) +--- /dev/null ++++ b/arch/ubicom32/include/asm/ocm_text.lds.inc +@@ -0,0 +1,175 @@ ++/* ++ * arch/ubicom32/include/asm/ocm_text.lds.inc ++ * <TODO: Replace with short file description> ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++*(.text.do_csum) ++*(.text.tcp_packet) ++*(.text.ipt_do_table) ++*(.text.nf_conntrack_in) ++*(.text.ip_forward) ++*(.text.dev_queue_xmit) ++*(.text.netif_receive_skb) ++*(.text.ip_route_input) ++*(.text.ip_finish_output) ++*(.text.nf_iterate) ++*(.text.__hash_conntrack) ++*(.text.memset) ++*(.text.memcpy) ++*(.text.ip_rcv) ++*(.text.__nf_conntrack_find) ++*(.text.dev_hard_start_xmit) ++*(.text.vlan_dev_hard_start_xmit) ++*(.text.vlan_dev_hard_header) ++*(.text.__nf_ct_refresh_acct) ++*(.text.tcp_error) ++*(.text.pfifo_fast_enqueue) ++*(.text.ipv4_confirm) ++*(.text.ip_output) ++*(.text.neigh_connected_output) ++*(.text.nf_hook_slow) ++*(.text.nf_nat_packet) ++*(.text.local_bh_enable) ++*(.text.pfifo_fast_dequeue) ++*(.text.ubi32_eth_receive) ++*(.text.nf_nat_fn) ++*(.text.skb_checksum) ++*(.text.memmove) ++*(.text.ubi32_eth_tx_done) ++*(.text.eth_header) ++*(.text.skb_release_data) ++*(.text.nf_conntrack_find_get) ++*(.text.process_backlog) ++*(.text.vlan_skb_recv) ++*(.text.ip_rcv_finish) ++*(.text.__qdisc_run) ++*(.text.skb_push) ++*(.text.eth_type_trans) ++*(.text.__alloc_skb) ++*(.text.netif_rx) ++*(.text.nf_ip_checksum) ++*(.text.__skb_checksum_complete_head) ++*(.text.ipv4_conntrack_defrag) ++*(.text.tcp_pkt_to_tuple) ++*(.text.kfree) ++*(.text.tcp_manip_pkt) ++*(.text.skb_put) ++*(.text.nf_ct_get_tuple) ++*(.text.__kmalloc) ++*(.text.ubi32_eth_start_xmit) ++*(.text.free_block) ++*(.text.ipt_hook) ++*(.text.kmem_cache_free) ++*(.text.skb_pull_rcsum) ++*(.text.cache_alloc_refill) ++*(.text.skb_release_head_state) ++*(.text.manip_pkt) ++*(.text.ip_sabotage_in) ++*(.text.ip_forward_finish) ++*(.text.kmem_cache_alloc) ++*(.text.local_bh_disable) ++*(.text.ipv4_pkt_to_tuple) ++*(.text.inet_proto_csum_replace4) ++*(.text.__nf_ct_l4proto_find) ++*(.text.csum_partial) ++*(.text.neigh_resolve_output) ++*(.text.__kfree_skb) ++*(.text.kfree_skb) ++*(.text.__find_vlan_dev) ++*(.text.ldsr_ctxsw_thread) ++*(.text.__do_IRQ) ++*(.text.skb_pull) ++*(.text.ipv4_invert_tuple) ++*(.text.nf_ct_invert_tuplepr) ++*(.text.skb_make_writable) ++*(.text.ipv4_get_l4proto) ++*(.text.handle_IRQ_event) ++*(.text.net_rx_action) ++*(.text.__do_softirq) ++*(.text.nf_nat_in) ++*(.text.note_interrupt) ++*(.text.ipv4_conntrack_in) ++*(.text.dst_release) ++*(.text.tasklet_action) ++*(.text.nf_nat_out) ++*(.text.nf_ct_invert_tuple) ++*(.text.do_IRQ) ++*(.text.__tasklet_schedule) ++*(.text.__skb_checksum_complete) ++*(.text.ubi32_eth_interrupt) ++*(.text.dev_kfree_skb_any) ++*(.text.ret_from_interrupt_to_kernel) ++*(.text.preemptive_context_save) ++*(.text.irq_ack_vector) ++*(.text.update_wall_time) ++*(.text.ldsr_thread) ++*(.text.irq_exit) ++*(.text.ubi32_eth_do_tasklet) ++*(.text.__napi_schedule) ++*(.text.idle_cpu) ++*(.text.run_timer_softirq) ++*(.text.ldsr_mask_vector) ++*(.text.irq_enter) ++*(.text.ldsr_get_lsb) ++*(.text.ldsr_unmask_vector) ++*(.text.ip_fast_csum) ++*(.text.hrtimer_run_queues) ++*(.text.tcp_invert_tuple) ++*(.text.T___705) ++*(.text.run_posix_cpu_timers) ++*(.text.free_hot_cold_page) ++*(.text.lock_timer_base) ++*(.text.calc_delta_mine) ++*(.text.slab_destroy) ++*(.text.rcu_pending) ++*(.text.scheduler_tick) ++*(.text.hrtimer_run_pending) ++*(.text.do_softirq) ++*(.text.del_timer) ++*(.text.irq_end_vector) ++*(.text.pci_read_u32) ++*(.text.udivmodsi4) ++*(.text.memcmp) ++*(.text.memset) ++*(.text.__slab_alloc) ++*(.text.br_handle_frame) ++*(.text.br_fdb_update) ++*(.text.__br_fdb_get) ++*(.text.br_forward) ++*(.text.br_handle_frame_finish) ++*(.text.pci_write_u32) ++*(.text.kmem_freepages) ++*(.text.br_dev_queue_push_xmit) ++*(.text.ioread32) ++*(.text.next_zones_zonelist) ++*(.text.ubi32_pci_read_u32) ++*(.text.zone_watermark_ok) ++*(.text.__rmqueue_smallest) ++*(.text.ubi32_eth_napi_poll) ++*(.text.ubi32_pci_write_u32) ++*(.text.ubi32_pci_read_u32) ++*(.text._local_bh_enable) ++*(.text._local_bh_disable) ++*(.text.get_slab) +--- /dev/null ++++ b/arch/ubicom32/include/asm/page.h +@@ -0,0 +1,106 @@ ++/* ++ * arch/ubicom32/include/asm/page.h ++ * Memory page related operations and definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_PAGE_H ++#define _ASM_UBICOM32_PAGE_H ++ ++/* PAGE_SHIFT determines the page size */ ++ ++#define PAGE_SHIFT 12 ++#define PAGE_SIZE (1 << PAGE_SHIFT) ++#define PAGE_MASK (~(PAGE_SIZE-1)) ++ ++#include <asm/setup.h> ++ ++#ifndef __ASSEMBLY__ ++ ++#define get_user_page(vaddr) __get_free_page(GFP_KERNEL) ++#define free_user_page(page, addr) free_page(addr) ++ ++#define clear_page(page) memset((page), 0, PAGE_SIZE) ++#define copy_page(to,from) memcpy((to), (from), PAGE_SIZE) ++ ++#define clear_user_page(page, vaddr, pg) clear_page(page) ++#define copy_user_page(to, from, vaddr, pg) copy_page(to, from) ++ ++#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \ ++ alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr) ++#define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE ++ ++/* ++ * These are used to make use of C type-checking.. ++ */ ++typedef struct { unsigned long pte; } pte_t; ++typedef struct { unsigned long pmd[16]; } pmd_t; ++typedef struct { unsigned long pgd; } pgd_t; ++typedef struct { unsigned long pgprot; } pgprot_t; ++typedef struct page *pgtable_t; ++ ++#define pte_val(x) ((x).pte) ++#define pmd_val(x) ((&x)->pmd[0]) ++#define pgd_val(x) ((x).pgd) ++#define pgprot_val(x) ((x).pgprot) ++ ++#define __pte(x) ((pte_t) { (x) } ) ++#define __pmd(x) ((pmd_t) { (x) } ) ++#define __pgd(x) ((pgd_t) { (x) } ) ++#define __pgprot(x) ((pgprot_t) { (x) } ) ++ ++extern unsigned long memory_start; ++extern unsigned long memory_end; ++ ++#endif /* !__ASSEMBLY__ */ ++ ++#include <asm/page_offset.h> ++ ++#define PAGE_OFFSET (PAGE_OFFSET_RAW) ++ ++#ifndef __ASSEMBLY__ ++ ++#define __pa(vaddr) virt_to_phys((void *)(vaddr)) ++#define __va(paddr) phys_to_virt((unsigned long)(paddr)) ++ ++#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) ++#define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT) ++ ++#define virt_to_page(addr) (mem_map + (((unsigned long)(addr)-PAGE_OFFSET) >> PAGE_SHIFT)) ++#define page_to_virt(page) ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) ++ ++#define pfn_to_page(pfn) virt_to_page(pfn_to_virt(pfn)) ++#define page_to_pfn(page) virt_to_pfn(page_to_virt(page)) ++#define pfn_valid(pfn) ((pfn) < max_mapnr) ++ ++#define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ ++ ((void *)(kaddr) < (void *)memory_end)) ++ ++#endif /* __ASSEMBLY__ */ ++ ++#ifdef __KERNEL__ ++#include <asm-generic/page.h> ++#endif ++ ++#endif /* _ASM_UBICOM32_PAGE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/page_offset.h +@@ -0,0 +1,35 @@ ++/* ++ * arch/ubicom32/include/asm/page_offset.h ++ * Definition of PAGE_OFFSET_RAW for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_PAGE_OFFSET_H ++#define _ASM_UBICOM32_PAGE_OFFSET_H ++ ++/* This handles the memory map.. */ ++#define PAGE_OFFSET_RAW 0x3ffc0000 ++ ++#endif /* _ASM_UBICOM32_PAGE_OFFSET_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/param.h +@@ -0,0 +1,49 @@ ++/* ++ * arch/ubicom32/include/asm/param.h ++ * Definition of miscellaneous constants, including HZ. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_PARAM_H ++#define _ASM_UBICOM32_PARAM_H ++ ++#ifdef __KERNEL__ ++#define HZ CONFIG_HZ ++#define USER_HZ HZ ++#define CLOCKS_PER_SEC (USER_HZ) ++#endif ++ ++#ifndef HZ ++#define HZ 100 ++#endif ++ ++#define EXEC_PAGESIZE 4096 ++ ++#ifndef NOGROUP ++#define NOGROUP (-1) ++#endif ++ ++#define MAXHOSTNAMELEN 64 /* max length of hostname */ ++ ++#endif /* _ASM_UBICOM32_PARAM_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/pci.h +@@ -0,0 +1,210 @@ ++/* ++ * arch/ubicom32/include/asm/pci.h ++ * Definitions of PCI operations for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_PCI_H ++#define _ASM_UBICOM32_PCI_H ++ ++#include <asm/io.h> ++ ++/* The PCI address space does equal the physical memory ++ * address space. The networking and block device layers use ++ * this boolean for bounce buffer decisions. ++ */ ++#define PCI_DMA_BUS_IS_PHYS (1) ++ ++ ++ ++/* ++ * Perform a master read/write to the PCI bus. ++ * These functions return a PCI_RESP_xxx code. ++ */ ++extern u8 pci_read_u32(u8 pci_cmd, u32 address, u32 *data); ++extern u8 pci_write_u32(u8 pci_cmd, u32 address, u32 data); ++extern u8 pci_read_u16(u8 pci_cmd, u32 address, u16 *data); ++extern u8 pci_write_u16(u8 pci_cmd, u32 address, u16 data); ++extern u8 pci_read_u8(u8 pci_cmd, u32 address, u8 *data); ++extern u8 pci_write_u8(u8 pci_cmd, u32 address, u8 data); ++ ++ ++#define PCIBIOS_MIN_IO 0x100 ++#define PCIBIOS_MIN_MEM 0x10000000 ++ ++#define pcibios_assign_all_busses() 0 ++#define pcibios_scan_all_fns(a, b) 0 ++extern void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, ++ struct resource *res); ++ ++extern void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, ++ struct pci_bus_region *region); ++ ++struct pci_sys_data; ++struct pci_bus; ++ ++struct hw_pci { ++ struct list_head buses; ++ int nr_controllers; ++ int (*setup)(int nr, struct pci_sys_data *); ++ struct pci_bus *(*scan)(int nr, struct pci_sys_data *); ++ void (*preinit)(void); ++ void (*postinit)(void); ++ u8 (*swizzle)(struct pci_dev *dev, u8 *pin); ++ int (*map_irq)(struct pci_dev *dev, u8 slot, u8 pin); ++}; ++ ++/* ++ * Per-controller structure ++ */ ++struct pci_sys_data { ++ struct list_head node; ++ int busnr; /* primary bus number */ ++ u64 mem_offset; /* bus->cpu memory mapping offset */ ++ unsigned long io_offset; /* bus->cpu IO mapping offset */ ++ struct pci_bus *bus; /* PCI bus */ ++ struct resource *resource[3]; /* Primary PCI bus resources */ ++ /* Bridge swizzling */ ++ u8 (*swizzle)(struct pci_dev *, u8 *); ++ /* IRQ mapping */ ++ int (*map_irq)(struct pci_dev *, u8, u8); ++ struct hw_pci *hw; ++}; ++ ++static inline struct resource * ++pcibios_select_root(struct pci_dev *pdev, struct resource *res) ++{ ++ struct resource *root = NULL; ++ ++ if (res->flags & IORESOURCE_IO) ++ root = &ioport_resource; ++ if (res->flags & IORESOURCE_MEM) ++ root = &iomem_resource; ++ ++ return root; ++} ++ ++static inline void pcibios_set_master(struct pci_dev *dev) ++{ ++ /* No special bus mastering setup handling */ ++} ++#define HAVE_ARCH_PCI_SET_DMA_MAX_SEGMENT_SIZE 1 ++#define HAVE_ARCH_PCI_SET_DMA_SEGMENT_BOUNDARY 1 ++ ++#ifdef CONFIG_PCI ++static inline void * pci_alloc_consistent(struct pci_dev *hwdev, size_t size, ++ dma_addr_t *dma_handle) ++{ ++ void *vaddr = kmalloc(size, GFP_KERNEL); ++ if(vaddr != NULL) { ++ *dma_handle = virt_to_phys(vaddr); ++ } ++ return vaddr; ++} ++ ++static inline int pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask) ++{ ++ return 1; ++} ++ ++static inline void pci_free_consistent(struct pci_dev *hwdev, size_t size, ++ void *cpu_addr, dma_addr_t dma_handle) ++{ ++ kfree(cpu_addr); ++ return; ++} ++ ++static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, ++ size_t size, int direction) ++{ ++ return virt_to_phys(ptr); ++} ++ ++static inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, ++ size_t size, int direction) ++{ ++ return; ++} ++ ++static inline dma_addr_t ++pci_map_page(struct pci_dev *hwdev, struct page *page, ++ unsigned long offset, size_t size, int direction) ++{ ++ return pci_map_single(hwdev, page_address(page) + offset, size, (int)direction); ++} ++ ++static inline void ++pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address, ++ size_t size, int direction) ++{ ++ pci_unmap_single(hwdev, dma_address, size, direction); ++} ++ ++static inline int ++pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, ++ int nents, int direction) ++{ ++ return nents; ++} ++ ++static inline void ++pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, ++ int nents, int direction) ++{ ++} ++ ++static inline void ++pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg, ++ int nelems, int direction) ++{ ++} ++ ++static inline void ++pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, ++ int nelems, int direction) ++{ ++} ++ ++static inline void ++pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t dma_handle, ++ size_t size, int direction) ++{ ++} ++ ++static inline void ++pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t dma_handle, ++ size_t size, int direction) ++{ ++} ++ ++static inline int ++pci_dma_mapping_error(struct pci_dev *hwdev, dma_addr_t dma_addr) ++{ ++ return dma_addr == 0; ++} ++extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); ++extern void pci_iounmap(struct pci_dev *dev, void __iomem *); ++#endif ++ ++#endif /* _ASM_UBICOM32_PCI_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/percpu.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/percpu.h ++ * Generic percpu.h for the Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_PERCPU_H ++#define _ASM_UBICOM32_PERCPU_H ++ ++#include <asm-generic/percpu.h> ++ ++#endif /* _ASM_UBICOM32_PERCPU_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/pgalloc.h +@@ -0,0 +1,36 @@ ++/* ++ * arch/ubicom32/include/asm/pgalloc.h ++ * Page table allocation definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_PGALLOC_H ++#define _ASM_UBICOM32_PGALLOC_H ++ ++#include <linux/mm.h> ++#include <asm/setup.h> ++ ++#define check_pgt_cache() do { } while (0) ++ ++#endif /* _ASM_UBICOM32_PGALLOC_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/pgtable.h +@@ -0,0 +1,124 @@ ++/* ++ * arch/ubicom32/include/asm/pgtable.h ++ * Ubicom32 pseudo page table definitions and operations. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2004 Microtronix Datacom Ltd ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * and various works, Alpha, ix86, M68K, Sparc, ...et al ++ */ ++#ifndef _ASM_UBICOM32_PGTABLE_H ++#define _ASM_UBICOM32_PGTABLE_H ++ ++#include <asm-generic/4level-fixup.h> ++ ++//vic - this bit copied from m68knommu version ++#include <asm/setup.h> ++#include <asm/io.h> ++#include <linux/sched.h> ++ ++typedef pte_t *pte_addr_t; ++ ++#define pgd_present(pgd) (1) /* pages are always present on NO_MM */ ++#define pgd_none(pgd) (0) ++#define pgd_bad(pgd) (0) ++#define pgd_clear(pgdp) ++#define kern_addr_valid(addr) (1) ++#define pmd_offset(a, b) ((void *)0) ++ ++#define PAGE_NONE __pgprot(0) /* these mean nothing to NO_MM */ ++#define PAGE_SHARED __pgprot(0) /* these mean nothing to NO_MM */ ++#define PAGE_COPY __pgprot(0) /* these mean nothing to NO_MM */ ++#define PAGE_READONLY __pgprot(0) /* these mean nothing to NO_MM */ ++#define PAGE_KERNEL __pgprot(0) /* these mean nothing to NO_MM */ ++//vic - this bit copied from m68knommu version ++ ++extern void paging_init(void); ++#define swapper_pg_dir ((pgd_t *) 0) ++ ++#define __swp_type(x) (0) ++#define __swp_offset(x) (0) ++#define __swp_entry(typ,off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) ++#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) ++#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) ++ ++/* ++ * pgprot_noncached() is only for infiniband pci support, and a real ++ * implementation for RAM would be more complicated. ++ */ ++#define pgprot_noncached(prot) (prot) ++ ++static inline int pte_file(pte_t pte) { return 0; } ++ ++/* ++ * ZERO_PAGE is a global shared page that is always zero: used ++ * for zero-mapped memory areas etc.. ++ */ ++#define ZERO_PAGE(vaddr) (virt_to_page(0)) ++ ++extern unsigned int kobjsize(const void *objp); ++extern int is_in_rom(unsigned long); ++ ++/* ++ * No page table caches to initialise ++ */ ++#define pgtable_cache_init() do { } while (0) ++ ++#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ ++ remap_pfn_range(vma, vaddr, pfn, size, prot) ++ ++extern inline void flush_cache_mm(struct mm_struct *mm) ++{ ++} ++ ++extern inline void flush_cache_range(struct mm_struct *mm, ++ unsigned long start, ++ unsigned long end) ++{ ++} ++ ++/* Push the page at kernel virtual address and clear the icache */ ++extern inline void flush_page_to_ram (unsigned long address) ++{ ++} ++ ++/* Push n pages at kernel virtual address and clear the icache */ ++extern inline void flush_pages_to_ram (unsigned long address, int n) ++{ ++} ++ ++/* ++ * All 32bit addresses are effectively valid for vmalloc... ++ * Sort of meaningless for non-VM targets. ++ */ ++#define VMALLOC_START 0 ++#define VMALLOC_END 0xffffffff ++ ++#define arch_enter_lazy_mmu_mode() do {} while (0) ++#define arch_leave_lazy_mmu_mode() do {} while (0) ++#define arch_flush_lazy_mmu_mode() do {} while (0) ++#define arch_enter_lazy_cpu_mode() do {} while (0) ++#define arch_leave_lazy_cpu_mode() do {} while (0) ++#define arch_flush_lazy_cpu_mode() do {} while (0) ++ ++#endif /* _ASM_UBICOM32_PGTABLE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/poll.h +@@ -0,0 +1,36 @@ ++/* ++ * arch/ubicom32/include/asm/poll.h ++ * Ubicom32 specific poll() related flags definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_POLL_H ++#define _ASM_UBICOM32_POLL_H ++ ++#define POLLWRNORM POLLOUT ++#define POLLWRBAND 0x0100 ++ ++#include <asm-generic/poll.h> ++ ++#endif /* _ASM_UBICOM32_POLL_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/posix_types.h +@@ -0,0 +1,93 @@ ++/* ++ * arch/ubicom32/include/asm/posix_types.h ++ * Ubicom32 architecture posix types. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2004 Microtronix Datacom Ltd ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef __ARCH_UBICOM32_POSIX_TYPES_H ++#define __ARCH_UBICOM32_POSIX_TYPES_H ++ ++/* ++ * This file is generally used by user-level software, so you need to ++ * be a little careful about namespace pollution etc. Also, we cannot ++ * assume GCC is being used. ++ */ ++ ++typedef unsigned long __kernel_ino_t; ++typedef unsigned short __kernel_mode_t; ++typedef unsigned short __kernel_nlink_t; ++typedef long __kernel_off_t; ++typedef int __kernel_pid_t; ++typedef unsigned short __kernel_ipc_pid_t; ++typedef unsigned short __kernel_uid_t; ++typedef unsigned short __kernel_gid_t; ++typedef unsigned int __kernel_size_t; ++typedef int __kernel_ssize_t; ++typedef int __kernel_ptrdiff_t; ++typedef long __kernel_time_t; ++typedef long __kernel_suseconds_t; ++typedef long __kernel_clock_t; ++typedef int __kernel_timer_t; ++typedef int __kernel_clockid_t; ++typedef int __kernel_daddr_t; ++typedef char * __kernel_caddr_t; ++typedef unsigned short __kernel_uid16_t; ++typedef unsigned short __kernel_gid16_t; ++typedef unsigned int __kernel_uid32_t; ++typedef unsigned int __kernel_gid32_t; ++ ++typedef unsigned short __kernel_old_uid_t; ++typedef unsigned short __kernel_old_gid_t; ++typedef unsigned short __kernel_old_dev_t; ++ ++#ifdef __GNUC__ ++typedef long long __kernel_loff_t; ++#endif ++ ++typedef struct { ++#if defined(__KERNEL__) || defined(__USE_ALL) ++ int val[2]; ++#else /* !defined(__KERNEL__) && !defined(__USE_ALL) */ ++ int __val[2]; ++#endif /* !defined(__KERNEL__) && !defined(__USE_ALL) */ ++} __kernel_fsid_t; ++ ++#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) ++ ++#undef __FD_SET ++#define __FD_SET(d, set) ((set)->fds_bits[__FDELT(d)] |= __FDMASK(d)) ++ ++#undef __FD_CLR ++#define __FD_CLR(d, set) ((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d)) ++ ++#undef __FD_ISSET ++#define __FD_ISSET(d, set) ((set)->fds_bits[__FDELT(d)] & __FDMASK(d)) ++ ++#undef __FD_ZERO ++#define __FD_ZERO(fdsetp) (memset (fdsetp, 0, sizeof(*(fd_set *)fdsetp))) ++ ++#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) */ ++ ++#endif +--- /dev/null ++++ b/arch/ubicom32/include/asm/processor.h +@@ -0,0 +1,163 @@ ++/* ++ * arch/ubicom32/include/asm/processor.h ++ * Thread related definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1995 Hamish Macdonald ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_PROCESSOR_H ++#define _ASM_UBICOM32_PROCESSOR_H ++ ++/* ++ * Default implementation of macro that returns current ++ * instruction pointer ("program counter"). ++ */ ++#define current_text_addr() ({ __label__ _l; _l: &&_l;}) ++ ++#include <linux/compiler.h> ++#include <linux/threads.h> ++#include <asm/types.h> ++#include <asm/segment.h> ++#include <asm/fpu.h> ++#include <asm/ptrace.h> ++#include <asm/current.h> ++#include <asm/thread_info.h> ++ ++#if defined(CONFIG_UBICOM32_V3) ++ #define CPU "IP5K" ++#endif ++#if defined(CONFIG_UBICOM32_V4) ++ #define CPU "IP7K" ++#endif ++#ifndef CPU ++ #define CPU "UNKNOWN" ++#endif ++ ++/* ++ * User space process size: 1st byte beyond user address space. ++ */ ++extern unsigned long memory_end; ++#define TASK_SIZE (memory_end) ++ ++/* ++ * This decides where the kernel will search for a free chunk of vm ++ * space during mmap's. We won't be using it ++ */ ++#define TASK_UNMAPPED_BASE 0 ++ ++/* ++ * This is the structure where we are going to save callee-saved registers. ++ * A5 is the return address, A7 is the stack pointer, A6 is the frame ++ * pointer. This is the frame that is created because of switch_to. This ++ * is not the frame due to interrupt preemption or because of syscall entry. ++ */ ++ ++struct thread_struct { ++ unsigned long d10; /* D10 */ ++ unsigned long d11; /* D11 */ ++ unsigned long d12; /* D12 */ ++ unsigned long d13; /* D13 */ ++ unsigned long a1; /* A1 */ ++ unsigned long a2; /* A2 */ ++ unsigned long a5; /* A5 return address. */ ++ unsigned long a6; /* A6 */ ++ unsigned long sp; /* A7 kernel stack pointer. */ ++}; ++ ++#define INIT_THREAD { \ ++ 0, 0, 0, 0, 0, 0, 0, 0, \ ++ sizeof(init_stack) + (unsigned long) init_stack - 8, \ ++} ++ ++/* ++ * Do necessary setup to start up a newly executed thread. ++ * ++ * pass the data segment into user programs if it exists, ++ * it can't hurt anything as far as I can tell ++ */ ++/* ++ * Do necessary setup to start up a newly executed thread. ++ */ ++#define start_thread(regs, new_pc, new_sp) \ ++ do { \ ++ regs->pc = new_pc & ~3; \ ++ regs->an[5] = new_pc & ~3; \ ++ regs->an[7] = new_sp; \ ++ regs->nesting_level = -1; \ ++ regs->frame_type = UBICOM32_FRAME_TYPE_NEW_THREAD; \ ++ regs->thread_type = NORMAL_THREAD; \ ++ } while(0) ++ ++/* Forward declaration, a strange C thing */ ++struct task_struct; ++ ++/* Free all resources held by a thread. */ ++static inline void release_thread(struct task_struct *dead_task) ++{ ++} ++ ++/* Prepare to copy thread state - unlazy all lazy status */ ++#define prepare_to_copy(tsk) do { } while (0) ++ ++extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); ++ ++/* ++ * Free current thread data structures etc.. ++ */ ++static inline void exit_thread(void) ++{ ++} ++ ++unsigned long thread_saved_pc(struct task_struct *tsk); ++unsigned long get_wchan(struct task_struct *p); ++ ++#define KSTK_EIP(tsk) (tsk->thread.a5) ++#define KSTK_ESP(tsk) (tsk->thread.sp) ++ ++#define cpu_relax() barrier() ++ ++extern void processor_init(void); ++extern unsigned int processor_timers(void); ++extern unsigned int processor_threads(void); ++extern unsigned int processor_frequency(void); ++extern int processor_interrupts(unsigned int *int0, unsigned int *int1); ++extern int processor_ocm(void **socm, void **eocm); ++extern int processor_dram(void **sdram, void **edram); ++ ++#define THREAD_SIZE_LONGS (THREAD_SIZE/sizeof(unsigned long)) ++#define KSTK_TOP(info) \ ++({ \ ++ unsigned long *__ptr = (unsigned long *)(info); \ ++ (unsigned long)(&__ptr[THREAD_SIZE_LONGS]); \ ++}) ++ ++#define task_pt_regs(task) \ ++({ \ ++ struct pt_regs *__regs__; \ ++ __regs__ = (struct pt_regs *)(KSTK_TOP(task_stack_page(task))-8); \ ++ __regs__ - 1; \ ++}) ++ ++#endif /* _ASM_UBICOM32_PROCESSOR_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ptrace.h +@@ -0,0 +1,177 @@ ++/* ++ * arch/ubicom32/include/asm/ptrace.h ++ * Ubicom32 architecture ptrace support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_PTRACE_H ++#define _ASM_UBICOM32_PTRACE_H ++ ++#ifndef __ASSEMBLY__ ++ ++/* ++ * We use hard coded constants because this is shared with user ++ * space and the values are NOT allowed to change. Only fields ++ * that are intended to be exposed get values. ++ */ ++#define PT_D0 0 ++#define PT_D1 4 ++#define PT_D2 8 ++#define PT_D3 12 ++#define PT_D4 16 ++#define PT_D5 20 ++#define PT_D6 24 ++#define PT_D7 28 ++#define PT_D8 32 ++#define PT_D9 36 ++#define PT_D10 40 ++#define PT_D11 44 ++#define PT_D12 48 ++#define PT_D13 52 ++#define PT_D14 56 ++#define PT_D15 60 ++#define PT_A0 64 ++#define PT_A1 68 ++#define PT_A2 72 ++#define PT_A3 76 ++#define PT_A4 80 ++#define PT_A5 84 ++#define PT_A6 88 ++#define PT_A7 92 ++#define PT_SP 92 ++#define PT_ACC0HI 96 ++#define PT_ACC0LO 100 ++#define PT_MAC_RC16 104 ++#define PT_ACC1HI 108 ++#define PT_ACC1LO 112 ++#define PT_SOURCE3 116 ++#define PT_INST_CNT 120 ++#define PT_CSR 124 ++#define PT_DUMMY_UNUSED 128 ++#define PT_INT_MASK0 132 ++#define PT_INT_MASK1 136 ++#define PT_TRAP_CAUSE 140 ++#define PT_PC 144 ++#define PT_ORIGINAL_D0 148 ++#define PT_FRAME_TYPE 152 ++ ++/* ++ * The following 'registers' are not registers at all but are used ++ * locate the relocated sections. ++ */ ++#define PT_TEXT_ADDR 200 ++#define PT_TEXT_END_ADDR 204 ++#define PT_DATA_ADDR 208 ++#define PT_EXEC_FDPIC_LOADMAP 212 ++#define PT_INTERP_FDPIC_LOADMAP 216 ++ ++/* ++ * This struct defines the way the registers are stored on the ++ * stack during a system call. ++ */ ++enum thread_type { ++ NORMAL_THREAD, ++ KERNEL_THREAD, ++}; ++ ++#define UBICOM32_FRAME_TYPE_SYSCALL -1 /* System call frame */ ++#define UBICOM32_FRAME_TYPE_INVALID 0 /* Invalid frame, no longer in use */ ++#define UBICOM32_FRAME_TYPE_INTERRUPT 1 /* Interrupt frame */ ++#define UBICOM32_FRAME_TYPE_TRAP 2 /* Trap frame */ ++#define UBICOM32_FRAME_TYPE_SIGTRAMP 3 /* Signal trampoline frame. */ ++#define UBICOM32_FRAME_TYPE_NEW_THREAD 4 /* New Thread. */ ++ ++struct pt_regs { ++ /* ++ * Data Registers ++ */ ++ unsigned long dn[16]; ++ ++ /* ++ * Address Registers ++ */ ++ unsigned long an[8]; ++ ++ /* ++ * Per thread misc registers. ++ */ ++ unsigned long acc0[2]; ++ unsigned long mac_rc16; ++ unsigned long acc1[2]; ++ unsigned long source3; ++ unsigned long inst_cnt; ++ unsigned long csr; ++ unsigned long dummy_unused; ++ unsigned long int_mask0; ++ unsigned long int_mask1; ++ unsigned long trap_cause; ++ unsigned long pc; ++ unsigned long original_dn_0; ++ ++ /* ++ * Frame type. Syscall frames are -1. For other types look above. ++ */ ++ unsigned long frame_type; ++ ++ /* ++ * These fields are not exposed to ptrace. ++ */ ++ unsigned long previous_pc; ++ long nesting_level; /* When the kernel in in user space this ++ * will be -1. */ ++ unsigned long thread_type; /* This indicates if this is a kernel ++ * thread. */ ++}; ++ ++/* ++ * This is the extended stack used by signal handlers and the context ++ * switcher: it's pushed after the normal "struct pt_regs". ++ */ ++struct switch_stack { ++ unsigned long dummy; ++}; ++ ++#ifdef __KERNEL__ ++ ++/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ ++#define PTRACE_GETREGS 12 ++#define PTRACE_SETREGS 13 ++ ++#ifndef PS_S ++#define PS_S (0x2000) ++#define PS_M (0x1000) ++#endif ++ ++extern int __user_mode(unsigned long sp); ++ ++#define user_mode(regs) (__user_mode((regs->an[7]))) ++#define user_stack(regs) ((regs)->an[7]) ++#define instruction_pointer(regs) ((regs)->pc) ++#define profile_pc(regs) instruction_pointer(regs) ++extern void show_regs(struct pt_regs *); ++#endif /* __KERNEL__ */ ++ ++#endif /* __ASSEMBLY__ */ ++ ++#endif /* _ASM_UBICOM32_PTRACE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/range-protect-asm.h +@@ -0,0 +1,91 @@ ++/* ++ * arch/ubicom32/include/asm/range-protect-asm.h ++ * Assembly macros for enabling memory protection. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_RANGE_PROTECT_ASM_H ++#define _ASM_UBICOM32_RANGE_PROTECT_ASM_H ++ ++#if defined(__ASSEMBLY__) ++ ++#include <asm/thread-asm.h> ++ ++/* ++ * You should only use the enable/disable ranges when you have the atomic lock, ++ * if you do not there will be problems. ++ */ ++ ++/* ++ * enable_kernel_ranges ++ * Enable the kernel ranges (disabling protection) for thread, ++ * where thread == (1 << thread number) ++ */ ++.macro enable_kernel_ranges thread ++#ifdef CONFIG_PROTECT_KERNEL ++ or.4 I_RANGE0_EN, I_RANGE0_EN, \thread /* Enable Range Register */ ++ or.4 D_RANGE0_EN, D_RANGE0_EN, \thread ++ or.4 D_RANGE1_EN, D_RANGE1_EN, \thread ++#endif ++.endm ++ ++/* ++ * enable_kernel_ranges_for_current ++ * Enable the kernel ranges (disabling protection) for this thread ++ */ ++.macro enable_kernel_ranges_for_current scratch_reg ++#ifdef CONFIG_PROTECT_KERNEL ++ thread_get_self_mask \scratch_reg ++ enable_kernel_ranges \scratch_reg ++#endif ++.endm ++ ++/* ++ * disable_kernel_ranges ++ * Disables the kernel ranges (enabling protection) for thread ++ * where thread == (1 << thread number) ++ */ ++.macro disable_kernel_ranges thread ++#ifdef CONFIG_PROTECT_KERNEL ++ not.4 \thread, \thread ++ and.4 I_RANGE0_EN, I_RANGE0_EN, \thread /* Disable Range Register */ ++ and.4 D_RANGE0_EN, D_RANGE0_EN, \thread ++ and.4 D_RANGE1_EN, D_RANGE1_EN, \thread ++#endif ++.endm ++ ++/* ++ * disable_kernel_ranges_for_current ++ * Disable kernel ranges (enabling protection) for this thread ++ */ ++.macro disable_kernel_ranges_for_current scratch_reg ++#ifdef CONFIG_PROTECT_KERNEL ++ thread_get_self_mask \scratch_reg ++ disable_kernel_ranges \scratch_reg ++#endif ++.endm ++#endif ++ ++#endif /* _ASM_UBICOM32_RANGE_PROTECT_ASM_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/range-protect.h +@@ -0,0 +1,62 @@ ++/* ++ * arch/ubicom32/include/asm/range-protect.h ++ * Assembly macros declared in C for enabling memory protection. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_RANGE_PROTECT_H ++#define _ASM_UBICOM32_RANGE_PROTECT_H ++ ++#if !defined(__ASSEMBLY__) ++#include <asm/thread.h> ++/* ++ * The following macros should be the identical to the ones in ++ * range-protect-asm.h ++ * ++ * You should only use the enable/disable ranges when you have the atomic lock, ++ * if you do not there will be problems. ++ */ ++ ++/* ++ * enable_kernel_ranges ++ * Enable the kernel ranges (disabling protection) for thread, ++ * where thread == (1 << thread number) ++ */ ++asm ( ++ ".macro enable_kernel_ranges thread \n\t" ++#ifdef CONFIG_PROTECT_KERNEL ++ " or.4 I_RANGE0_EN, I_RANGE0_EN, \\thread \n\t" /* Enable Range Register */ ++ " or.4 D_RANGE0_EN, D_RANGE0_EN, \\thread \n\t" ++ " or.4 D_RANGE1_EN, D_RANGE1_EN, \\thread \n\t" ++#endif ++ ".endm \n\t" ++); ++ ++#else /* __ASSEMBLY__ */ ++ ++#include <asm/range-protect-asm.h> ++ ++#endif ++#endif /* _ASM_UBICOM32_RANGE_PROTECT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/resource.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/resource.h ++ * Generic definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_RESOURCE_H ++#define _ASM_UBICOM32_RESOURCE_H ++ ++#include <asm-generic/resource.h> ++ ++#endif /* _ASM_UBICOM32_RESOURCE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ring_tio.h +@@ -0,0 +1,42 @@ ++/* ++ * arch/ubicom32/include/asm/ring_tio.h ++ * Ubicom32 architecture Ring TIO definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++#ifndef _ASM_UBICOM32_RING_TIO_H ++#define _ASM_UBICOM32_RING_TIO_H ++ ++#include <asm/devtree.h> ++ ++#define RING_TIO_NODE_VERSION 2 ++ ++/* ++ * Devtree node for ring ++ */ ++struct ring_tio_node { ++ struct devtree_node dn; ++ ++ u32_t version; ++ void *regs; ++}; ++ ++extern void ring_tio_init(const char *node_name); ++ ++#endif /* _ASM_UBICOM32_RING_TIO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/scatterlist.h +@@ -0,0 +1,49 @@ ++/* ++ * arch/ubicom32/include/asm/scatterlist.h ++ * Definitions of struct scatterlist for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SCATTERLIST_H ++#define _ASM_UBICOM32_SCATTERLIST_H ++ ++#include <linux/mm.h> ++#include <asm/types.h> ++ ++struct scatterlist { ++#ifdef CONFIG_DEBUG_SG ++ unsigned long sg_magic; ++#endif ++ unsigned long page_link; ++ unsigned int offset; ++ dma_addr_t dma_address; ++ unsigned int length; ++}; ++ ++#define sg_dma_address(sg) ((sg)->dma_address) ++#define sg_dma_len(sg) ((sg)->length) ++ ++#define ISA_DMA_THRESHOLD (0xffffffff) ++ ++#endif /* _ASM_UBICOM32_SCATTERLIST_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/sd_tio.h +@@ -0,0 +1,36 @@ ++/* ++ * arch/ubicom32/include/asm/sd_tio.h ++ * SD TIO definitions ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++#ifndef _ASM_UBICOM32_SD_TIO_H ++#define _ASM_UBICOM32_SD_TIO_H ++ ++#include <asm/devtree.h> ++ ++/* ++ * Devtree node for SD ++ */ ++struct sd_tio_node { ++ struct devtree_node dn; ++ void *regs; ++}; ++ ++#endif /* _ASM_UBICOM32_SD_TIO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/sections.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/sections.h ++ * Generic sections.h definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SECTIONS_H ++#define _ASM_UBICOM32_SECTIONS_H ++ ++#include <asm-generic/sections.h> ++ ++#endif /* _ASM_UBICOM32_SECTIONS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/segment.h +@@ -0,0 +1,78 @@ ++/* ++ * arch/ubicom32/include/asm/segment.h ++ * Memory segment definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SEGMENT_H ++#define _ASM_UBICOM32_SEGMENT_H ++ ++/* define constants */ ++/* Address spaces (FC0-FC2) */ ++#define USER_DATA (1) ++#ifndef __USER_DS ++#define __USER_DS (USER_DATA) ++#endif ++#define USER_PROGRAM (2) ++#define SUPER_DATA (5) ++#ifndef __KERNEL_DS ++#define __KERNEL_DS (SUPER_DATA) ++#endif ++#define SUPER_PROGRAM (6) ++#define CPU_SPACE (7) ++ ++#ifndef __ASSEMBLY__ ++ ++typedef struct { ++ unsigned long seg; ++} mm_segment_t; ++ ++#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) ++#define USER_DS MAKE_MM_SEG(__USER_DS) ++#define KERNEL_DS MAKE_MM_SEG(__KERNEL_DS) ++ ++/* ++ * Get/set the SFC/DFC registers for MOVES instructions ++ */ ++ ++static inline mm_segment_t get_fs(void) ++{ ++ return USER_DS; ++} ++ ++static inline mm_segment_t get_ds(void) ++{ ++ /* return the supervisor data space code */ ++ return KERNEL_DS; ++} ++ ++static inline void set_fs(mm_segment_t val) ++{ ++} ++ ++#define segment_eq(a,b) ((a).seg == (b).seg) ++ ++#endif /* __ASSEMBLY__ */ ++ ++#endif /* _ASM_UBICOM32_SEGMENT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/semaphore.h +@@ -0,0 +1,140 @@ ++/* ++ * arch/ubicom32/include/asm/semaphore.h ++ * Interrupt-safe semaphores for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * (C) Copyright 1996 Linus Torvalds ++ * m68k version by Andreas Schwab ++ * Copyright (C) 2004 Microtronix Datacom Ltd ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SEMAPHORE_H ++#define _ASM_UBICOM32_SEMAPHORE_H ++ ++#define RW_LOCK_BIAS 0x01000000 ++ ++#ifndef __ASSEMBLY__ ++ ++#include <linux/linkage.h> ++#include <linux/wait.h> ++#include <linux/spinlock.h> ++#include <linux/rwsem.h> ++ ++#include <asm/system.h> ++#include <asm/atomic.h> ++ ++struct semaphore { ++ atomic_t count; ++ atomic_t waking; ++ wait_queue_head_t wait; ++}; ++ ++#define __SEMAPHORE_INITIALIZER(name, n) \ ++{ \ ++ .count = ATOMIC_INIT(n), \ ++ .waking = ATOMIC_INIT(0), \ ++ .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ ++} ++ ++#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ ++ struct semaphore name = __SEMAPHORE_INITIALIZER(name,count) ++ ++#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) ++#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0) ++ ++static inline void sema_init (struct semaphore *sem, int val) ++{ ++ *sem = (struct semaphore)__SEMAPHORE_INITIALIZER(*sem, val); ++} ++ ++static inline void init_MUTEX (struct semaphore *sem) ++{ ++ sema_init(sem, 1); ++} ++ ++static inline void init_MUTEX_LOCKED (struct semaphore *sem) ++{ ++ sema_init(sem, 0); ++} ++ ++asmlinkage void __down_failed(void /* special register calling convention */); ++asmlinkage int __down_failed_interruptible(void /* params in registers */); ++asmlinkage int __down_failed_trylock(void /* params in registers */); ++asmlinkage void __up_wakeup(void /* special register calling convention */); ++ ++asmlinkage void __down(struct semaphore * sem); ++asmlinkage int __down_interruptible(struct semaphore * sem); ++asmlinkage int __down_trylock(struct semaphore * sem); ++asmlinkage void __up(struct semaphore * sem); ++ ++extern spinlock_t semaphore_wake_lock; ++ ++/* ++ * This is ugly, but we want the default case to fall through. ++ * "down_failed" is a special asm handler that calls the C ++ * routine that actually waits. ++ */ ++static inline void down(struct semaphore * sem) ++{ ++ might_sleep(); ++ ++ if (atomic_dec_return(&sem->count) < 0) ++ __down(sem); ++} ++ ++static inline int down_interruptible(struct semaphore * sem) ++{ ++ int ret = 0; ++ ++ ++ might_sleep(); ++ ++ if(atomic_dec_return(&sem->count) < 0) ++ ret = __down_interruptible(sem); ++ return ret; ++} ++ ++static inline int down_trylock(struct semaphore * sem) ++{ ++ int ret = 0; ++ ++ if (atomic_dec_return (&sem->count) < 0) ++ ret = __down_trylock(sem); ++ return ret; ++} ++ ++/* ++ * Note! This is subtle. We jump to wake people up only if ++ * the semaphore was negative (== somebody was waiting on it). ++ * The default case (no contention) will result in NO ++ * jumps for both down() and up(). ++ */ ++static inline void up(struct semaphore * sem) ++{ ++ if (atomic_inc_return(&sem->count) <= 0) ++ __up(sem); ++} ++ ++#endif /* __ASSEMBLY__ */ ++ ++#endif /* _ASM_UBICOM32_SEMAPHORE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/semaphore-helper.h +@@ -0,0 +1,109 @@ ++/* ++ * arch/ubicom32/include/asm/semaphore-helper.h ++ * Semaphore related definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SEMAPHORE_HELPER_H ++#define _ASM_UBICOM32_SEMAPHORE_HELPER_H ++ ++/* ++ * SMP- and interrupt-safe semaphores helper functions. ++ * ++ * (C) Copyright 1996 Linus Torvalds ++ * ++ * m68k version by Andreas Schwab ++ */ ++ ++ ++/* ++ * These two _must_ execute atomically wrt each other. ++ */ ++static inline void wake_one_more(struct semaphore * sem) ++{ ++ atomic_inc(&sem->waking); ++} ++ ++static inline int waking_non_zero(struct semaphore *sem) ++{ ++ int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&semaphore_wake_lock, flags); ++ ret = 0; ++ if (atomic_read(&sem->waking) > 0) { ++ atomic_dec(&sem->waking); ++ ret = 1; ++ } ++ spin_unlock_irqrestore(&semaphore_wake_lock, flags); ++ return ret; ++} ++ ++/* ++ * waking_non_zero_interruptible: ++ * 1 got the lock ++ * 0 go to sleep ++ * -EINTR interrupted ++ */ ++static inline int waking_non_zero_interruptible(struct semaphore *sem, ++ struct task_struct *tsk) ++{ ++ int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&semaphore_wake_lock, flags); ++ ret = 0; ++ if (atomic_read(&sem->waking) > 0) { ++ atomic_dec(&sem->waking); ++ ret = 1; ++ } else if (signal_pending(tsk)) { ++ atomic_inc(&sem->count); ++ ret = -EINTR; ++ } ++ spin_unlock_irqrestore(&semaphore_wake_lock, flags); ++ return ret; ++} ++ ++/* ++ * waking_non_zero_trylock: ++ * 1 failed to lock ++ * 0 got the lock ++ */ ++static inline int waking_non_zero_trylock(struct semaphore *sem) ++{ ++ int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&semaphore_wake_lock, flags); ++ ret = 1; ++ if (atomic_read(&sem->waking) > 0) { ++ atomic_dec(&sem->waking); ++ ret = 0; ++ } else ++ atomic_inc(&sem->count); ++ spin_unlock_irqrestore(&semaphore_wake_lock, flags); ++ return ret; ++} ++ ++#endif /* _ASM_UBICOM32_SEMAPHORE_HELPER_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/sembuf.h +@@ -0,0 +1,52 @@ ++/* ++ * arch/ubicom32/include/asm/sembuf.h ++ * The semid64_ds structure for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SEMBUF_H ++#define _ASM_UBICOM32_SEMBUF_H ++ ++/* ++ * The semid64_ds structure for ubicom32 architecture. ++ * Note extra padding because this structure is passed back and forth ++ * between kernel and user space. ++ * ++ * Pad space is left for: ++ * - 64-bit time_t to solve y2038 problem ++ * - 2 miscellaneous 32-bit values ++ */ ++ ++struct semid64_ds { ++ struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ ++ __kernel_time_t sem_otime; /* last semop time */ ++ unsigned long __unused1; ++ __kernel_time_t sem_ctime; /* last change time */ ++ unsigned long __unused2; ++ unsigned long sem_nsems; /* no. of semaphores in array */ ++ unsigned long __unused3; ++ unsigned long __unused4; ++}; ++ ++#endif /* _ASM_UBICOM32_SEMBUF_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/setup.h +@@ -0,0 +1,35 @@ ++/* ++ * arch/ubicom32/include/asm/setup.h ++ * Kernel command line length definition. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2004, Microtronix Datacom Ltd., All rights reserved. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_SETUP_H ++#define _ASM_UBICOM32_SETUP_H ++ ++#define COMMAND_LINE_SIZE 512 ++ ++#endif /* _ASM_UBICOM32_SETUP_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/shmbuf.h +@@ -0,0 +1,69 @@ ++/* ++ * arch/ubicom32/include/asm/shmbuf.h ++ * The shmid64_ds structure for the Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SHMBUF_H ++#define _ASM_UBICOM32_SHMBUF_H ++ ++/* ++ * The shmid64_ds structure for m68k architecture. ++ * Note extra padding because this structure is passed back and forth ++ * between kernel and user space. ++ * ++ * Pad space is left for: ++ * - 64-bit time_t to solve y2038 problem ++ * - 2 miscellaneous 32-bit values ++ */ ++ ++struct shmid64_ds { ++ struct ipc64_perm shm_perm; /* operation perms */ ++ size_t shm_segsz; /* size of segment (bytes) */ ++ __kernel_time_t shm_atime; /* last attach time */ ++ unsigned long __unused1; ++ __kernel_time_t shm_dtime; /* last detach time */ ++ unsigned long __unused2; ++ __kernel_time_t shm_ctime; /* last change time */ ++ unsigned long __unused3; ++ __kernel_pid_t shm_cpid; /* pid of creator */ ++ __kernel_pid_t shm_lpid; /* pid of last operator */ ++ unsigned long shm_nattch; /* no. of current attaches */ ++ unsigned long __unused4; ++ unsigned long __unused5; ++}; ++ ++struct shminfo64 { ++ unsigned long shmmax; ++ unsigned long shmmin; ++ unsigned long shmmni; ++ unsigned long shmseg; ++ unsigned long shmall; ++ unsigned long __unused1; ++ unsigned long __unused2; ++ unsigned long __unused3; ++ unsigned long __unused4; ++}; ++ ++#endif /* _ASM_UBICOM32_SHMBUF_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/shmparam.h +@@ -0,0 +1,35 @@ ++/* ++ * arch/ubicom32/include/asm/shmparam.h ++ * Shared memory definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2004 Microtronix Datacom Ltd ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * Alpha, ix86, M68K, Sparc, ...et al ++ */ ++#ifndef _ASM_UBICOM32_SHMPARAM_H ++#define _ASM_UBICOM32_SHMPARAM_H ++ ++#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ ++ ++#endif /* _ASM_UBICOM32_SHMPARAM_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/sigcontext.h +@@ -0,0 +1,37 @@ ++/* ++ * arch/ubicom32/include/asm/sigcontext.h ++ * Definition of sigcontext struct for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SIGCONTEXT_H ++#define _ASM_UBICOM32_SIGCONTEXT_H ++ ++#include <asm/ptrace.h> ++ ++struct sigcontext { ++ struct pt_regs sc_regs; ++}; ++ ++#endif /* _ASM_UBICOM32_SIGCONTEXT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/siginfo.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/siginfo.h ++ * Generic siginfo.h definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SIGINFO_H ++#define _ASM_UBICOM32_SIGINFO_H ++ ++#include <asm-generic/siginfo.h> ++ ++#endif /* _ASM_UBICOM32_SIGINFO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/signal.h +@@ -0,0 +1,180 @@ ++/* ++ * arch/ubicom32/include/asm/signal.h ++ * Signal related definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SIGNAL_H ++#define _ASM_UBICOM32_SIGNAL_H ++ ++#include <linux/types.h> ++ ++/* Avoid too many header ordering problems. */ ++struct siginfo; ++ ++#ifdef __KERNEL__ ++/* Most things should be clean enough to redefine this at will, if care ++ is taken to make libc match. */ ++ ++#define _NSIG 64 ++#define _NSIG_BPW 32 ++#define _NSIG_WORDS (_NSIG / _NSIG_BPW) ++ ++typedef unsigned long old_sigset_t; /* at least 32 bits */ ++ ++typedef struct { ++ unsigned long sig[_NSIG_WORDS]; ++} sigset_t; ++ ++#endif /* __KERNEL__ */ ++ ++#define SIGHUP 1 ++#define SIGINT 2 ++#define SIGQUIT 3 ++#define SIGILL 4 ++#define SIGTRAP 5 ++#define SIGABRT 6 ++#define SIGIOT 6 ++#define SIGBUS 7 ++#define SIGFPE 8 ++#define SIGKILL 9 ++#define SIGUSR1 10 ++#define SIGSEGV 11 ++#define SIGUSR2 12 ++#define SIGPIPE 13 ++#define SIGALRM 14 ++#define SIGTERM 15 ++#define SIGSTKFLT 16 ++#define SIGCHLD 17 ++#define SIGCONT 18 ++#define SIGSTOP 19 ++#define SIGTSTP 20 ++#define SIGTTIN 21 ++#define SIGTTOU 22 ++#define SIGURG 23 ++#define SIGXCPU 24 ++#define SIGXFSZ 25 ++#define SIGVTALRM 26 ++#define SIGPROF 27 ++#define SIGWINCH 28 ++#define SIGIO 29 ++#define SIGPOLL SIGIO ++/* ++#define SIGLOST 29 ++*/ ++#define SIGPWR 30 ++#define SIGSYS 31 ++#define SIGUNUSED 31 ++ ++/* These should not be considered constants from userland. */ ++#define SIGRTMIN 32 ++#define SIGRTMAX _NSIG ++ ++/* ++ * SA_FLAGS values: ++ * ++ * SA_ONSTACK indicates that a registered stack_t will be used. ++ * SA_RESTART flag to get restarting signals (which were the default long ago) ++ * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. ++ * SA_RESETHAND clears the handler when the signal is delivered. ++ * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. ++ * SA_NODEFER prevents the current signal from being masked in the handler. ++ * ++ * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single ++ * Unix names RESETHAND and NODEFER respectively. ++ */ ++#define SA_NOCLDSTOP 0x00000001 ++#define SA_NOCLDWAIT 0x00000002 ++#define SA_SIGINFO 0x00000004 ++#define SA_ONSTACK 0x08000000 ++#define SA_RESTART 0x10000000 ++#define SA_NODEFER 0x40000000 ++#define SA_RESETHAND 0x80000000 ++ ++#define SA_NOMASK SA_NODEFER ++#define SA_ONESHOT SA_RESETHAND ++ ++/* ++ * sigaltstack controls ++ */ ++#define SS_ONSTACK 1 ++#define SS_DISABLE 2 ++ ++#define MINSIGSTKSZ 2048 ++#define SIGSTKSZ 8192 ++ ++#include <asm-generic/signal.h> ++ ++#ifdef __KERNEL__ ++struct old_sigaction { ++ __sighandler_t sa_handler; ++ old_sigset_t sa_mask; ++ unsigned long sa_flags; ++ void (*sa_restorer)(void); ++}; ++ ++struct sigaction { ++ __sighandler_t sa_handler; ++ unsigned long sa_flags; ++ void (*sa_restorer)(void); ++ sigset_t sa_mask; /* mask last for extensibility */ ++}; ++ ++struct k_sigaction { ++ struct sigaction sa; ++}; ++#else ++/* Here we must cater to libcs that poke about in kernel headers. */ ++ ++struct sigaction { ++ union { ++ __sighandler_t _sa_handler; ++ void (*_sa_sigaction)(int, struct siginfo *, void *); ++ } _u; ++ sigset_t sa_mask; ++ unsigned long sa_flags; ++ void (*sa_restorer)(void); ++}; ++ ++#define sa_handler _u._sa_handler ++#define sa_sigaction _u._sa_sigaction ++ ++#endif /* __KERNEL__ */ ++ ++typedef struct sigaltstack { ++ void *ss_sp; ++ int ss_flags; ++ size_t ss_size; ++} stack_t; ++ ++#ifdef __KERNEL__ ++ ++#include <asm/sigcontext.h> ++#undef __HAVE_ARCH_SIG_BITOPS ++ ++#define ptrace_signal_deliver(regs, cookie) do { } while (0) ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _ASM_UBICOM32_SIGNAL_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/smp.h +@@ -0,0 +1,83 @@ ++/* ++ * arch/ubicom32/include/asm/smp.h ++ * SMP definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SMP_H ++#define _ASM_UBICOM32_SMP_H ++ ++#ifndef ASSEMBLY ++#include <linux/bitops.h> ++#include <linux/threads.h> ++#include <linux/cpumask.h> ++#include <asm/ip5000.h> ++ ++typedef unsigned long address_t; ++extern cpumask_t cpu_online_map; ++extern unsigned int smp_ipi_irq; ++ ++/* ++ * This magic constant controls our willingness to transfer ++ * a process across CPUs. ++ * ++ * Such a transfer incurs cache and tlb ++ * misses. The current value is inherited from i386. Still needs ++ * to be tuned for parisc. ++ */ ++#define PROC_CHANGE_PENALTY 15 /* Schedule penalty */ ++#define NO_PROC_ID 0xFF /* No processor magic marker */ ++#define ANY_PROC_ID 0xFF /* Any processor magic marker */ ++ ++#ifdef CONFIG_SMP ++#define raw_smp_processor_id() (current_thread_info()->cpu) ++#endif /* CONFIG_SMP */ ++ ++static inline int __cpu_disable (void) ++{ ++ return 0; ++} ++ ++static inline void __cpu_die (unsigned int cpu) ++{ ++ while(1) { ++ }; ++} ++ ++extern int __cpu_up(unsigned int cpu); ++extern void smp_send_timer_all(void); ++extern void smp_timer_broadcast(cpumask_t mask); ++extern void smp_set_affinity(unsigned int irq, cpumask_t dest); ++extern void arch_send_call_function_single_ipi(int cpu); ++extern void arch_send_call_function_ipi(cpumask_t mask); ++ ++/* ++ * TODO: Once these are fully tested, we should turn them into ++ * inline macros for performance. ++ */ ++extern unsigned long smp_get_affinity(unsigned int irq, int *all); ++extern void smp_reset_ipi(unsigned long mask); ++ ++#endif /* !ASSEMBLY */ ++#endif /* _ASM_UBICOM32_SMP_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/socket.h +@@ -0,0 +1,84 @@ ++/* ++ * arch/ubicom32/include/asm/socket.h ++ * Socket options definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SOCKET_H ++#define _ASM_UBICOM32_SOCKET_H ++ ++#include <asm/sockios.h> ++ ++/* For setsockopt(2) */ ++#define SOL_SOCKET 1 ++ ++#define SO_DEBUG 1 ++#define SO_REUSEADDR 2 ++#define SO_TYPE 3 ++#define SO_ERROR 4 ++#define SO_DONTROUTE 5 ++#define SO_BROADCAST 6 ++#define SO_SNDBUF 7 ++#define SO_RCVBUF 8 ++#define SO_SNDBUFFORCE 32 ++#define SO_RCVBUFFORCE 33 ++#define SO_KEEPALIVE 9 ++#define SO_OOBINLINE 10 ++#define SO_NO_CHECK 11 ++#define SO_PRIORITY 12 ++#define SO_LINGER 13 ++#define SO_BSDCOMPAT 14 ++/* To add :#define SO_REUSEPORT 15 */ ++#define SO_PASSCRED 16 ++#define SO_PEERCRED 17 ++#define SO_RCVLOWAT 18 ++#define SO_SNDLOWAT 19 ++#define SO_RCVTIMEO 20 ++#define SO_SNDTIMEO 21 ++ ++/* Security levels - as per NRL IPv6 - don't actually do anything */ ++#define SO_SECURITY_AUTHENTICATION 22 ++#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 ++#define SO_SECURITY_ENCRYPTION_NETWORK 24 ++ ++#define SO_BINDTODEVICE 25 ++ ++/* Socket filtering */ ++#define SO_ATTACH_FILTER 26 ++#define SO_DETACH_FILTER 27 ++ ++#define SO_PEERNAME 28 ++#define SO_TIMESTAMP 29 ++#define SCM_TIMESTAMP SO_TIMESTAMP ++ ++#define SO_ACCEPTCONN 30 ++ ++#define SO_PEERSEC 31 ++#define SO_PASSSEC 34 ++#define SO_TIMESTAMPNS 35 ++#define SCM_TIMESTAMPNS SO_TIMESTAMPNS ++ ++#define SO_MARK 36 ++ ++#endif /* _ASM_UBICOM32_SOCKET_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/sockios.h +@@ -0,0 +1,40 @@ ++/* ++ * arch/ubicom32/include/asm/sockios.h ++ * Socket-level ioctl definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SOCKIOS_H ++#define _ASM_UBICOM32_SOCKIOS_H ++ ++/* Socket-level I/O control calls. */ ++#define FIOSETOWN 0x8901 ++#define SIOCSPGRP 0x8902 ++#define FIOGETOWN 0x8903 ++#define SIOCGPGRP 0x8904 ++#define SIOCATMARK 0x8905 ++#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ ++#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ ++ ++#endif /* _ASM_UBICOM32_SOCKIOS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/spinlock.h +@@ -0,0 +1,293 @@ ++/* ++ * arch/ubicom32/include/asm/spinlock.h ++ * Spinlock related definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SPINLOCK_H ++#define _ASM_UBICOM32_SPINLOCK_H ++ ++#include <asm/system.h> ++#include <asm/processor.h> ++#include <asm/spinlock_types.h> ++ ++/* ++ * __raw_spin_lock() ++ * Lock the lock. ++ */ ++static inline void __raw_spin_lock(raw_spinlock_t *x) ++{ ++ asm volatile ( ++ "1: bset %0, %0, #0 \n\t" ++ " jmpne.f 1b \n\t" ++ : "+U4" (x->lock) ++ : ++ : "memory", "cc" ++ ); ++} ++ ++/* ++ * __raw_spin_unlock() ++ * Unlock the lock. ++ */ ++static inline void __raw_spin_unlock(raw_spinlock_t *x) ++{ ++ asm volatile ( ++ " bclr %0, %0, #0 \n\t" ++ : "+U4" (x->lock) ++ : ++ : "memory", "cc" ++ ); ++} ++ ++/* ++ * __raw_spin_is_locked() ++ * Test if the lock is locked. ++ */ ++static inline int __raw_spin_is_locked(raw_spinlock_t *x) ++{ ++ return x->lock; ++} ++ ++/* ++ * __raw_spin_unlock_wait() ++ * Wait for the lock to be unlocked. ++ * ++ * Note: the caller has not guarantee that the lock will not ++ * be acquired before they get to it. ++ */ ++static inline void __raw_spin_unlock_wait(raw_spinlock_t *x) ++{ ++ do { ++ cpu_relax(); ++ } while (__raw_spin_is_locked(x)); ++} ++ ++/* ++ * __raw_spin_trylock() ++ * Try the lock, return 0 on failure, 1 on success. ++ */ ++static inline int __raw_spin_trylock(raw_spinlock_t *x) ++{ ++ int ret = 0; ++ ++ asm volatile ( ++ " bset %1, %1, #0 \n\t" ++ " jmpne.f 1f \n\t" ++ " move.4 %0, #1 \n\t" ++ "1: \n\t" ++ : "+r" (ret), "+U4" (x->lock) ++ : ++ : "memory", "cc" ++ ); ++ ++ return ret; ++} ++ ++/* ++ * __raw_spin_lock_flags() ++ * Spin waiting for the lock (enabling IRQ(s)) ++ */ ++static inline void __raw_spin_lock_flags(raw_spinlock_t *x, unsigned long flags) ++{ ++ mb(); ++ while (!__raw_spin_trylock(x)) { ++ /* ++ * If the flags from the IRQ are set, interrupts are disabled and we ++ * need to re-enable them. ++ */ ++ if (!flags) { ++ cpu_relax(); ++ } else { ++ raw_local_irq_enable(); ++ cpu_relax(); ++ raw_local_irq_disable(); ++ } ++ } ++ mb(); ++} ++ ++/* ++ * Read-write spinlocks, allowing multiple readers but only one writer. ++ * Linux rwlocks are unfair to writers; they can be starved for an indefinite ++ * time by readers. With care, they can also be taken in interrupt context. ++ * ++ * In Ubicom32 architecture implementation, we have a spinlock and a counter. ++ * Readers use the lock to serialise their access to the counter (which ++ * records how many readers currently hold the lock). ++ * Writers hold the spinlock, preventing any readers or other writers from ++ * grabbing the rwlock. ++ */ ++ ++/* ++ * __raw_read_lock() ++ * Increment the counter in the rwlock. ++ * ++ * Note that we have to ensure interrupts are disabled in case we're ++ * interrupted by some other code that wants to grab the same read lock ++ */ ++static inline void __raw_read_lock(raw_rwlock_t *rw) ++{ ++ unsigned long flags; ++ raw_local_irq_save(flags); ++ __raw_spin_lock_flags(&rw->lock, flags); ++ rw->counter++; ++ __raw_spin_unlock(&rw->lock); ++ raw_local_irq_restore(flags); ++} ++ ++/* ++ * __raw_read_unlock() ++ * Decrement the counter. ++ * ++ * Note that we have to ensure interrupts are disabled in case we're ++ * interrupted by some other code that wants to grab the same read lock ++ */ ++static inline void __raw_read_unlock(raw_rwlock_t *rw) ++{ ++ unsigned long flags; ++ raw_local_irq_save(flags); ++ __raw_spin_lock_flags(&rw->lock, flags); ++ rw->counter--; ++ __raw_spin_unlock(&rw->lock); ++ raw_local_irq_restore(flags); ++} ++ ++/* ++ * __raw_read_trylock() ++ * Increment the counter if we can. ++ * ++ * Note that we have to ensure interrupts are disabled in case we're ++ * interrupted by some other code that wants to grab the same read lock ++ */ ++static inline int __raw_read_trylock(raw_rwlock_t *rw) ++{ ++ unsigned long flags; ++ retry: ++ raw_local_irq_save(flags); ++ if (__raw_spin_trylock(&rw->lock)) { ++ rw->counter++; ++ __raw_spin_unlock(&rw->lock); ++ raw_local_irq_restore(flags); ++ return 1; ++ } ++ ++ raw_local_irq_restore(flags); ++ ++ /* ++ * If write-locked, we fail to acquire the lock ++ */ ++ if (rw->counter < 0) { ++ return 0; ++ } ++ ++ /* ++ * Wait until we have a realistic chance at the lock ++ */ ++ while (__raw_spin_is_locked(&rw->lock) && rw->counter >= 0) { ++ cpu_relax(); ++ } ++ ++ goto retry; ++} ++ ++/* ++ * __raw_write_lock() ++ * ++ * Note that we have to ensure interrupts are disabled in case we're ++ * interrupted by some other code that wants to read_trylock() this lock ++ */ ++static inline void __raw_write_lock(raw_rwlock_t *rw) ++{ ++ unsigned long flags; ++retry: ++ raw_local_irq_save(flags); ++ __raw_spin_lock_flags(&rw->lock, flags); ++ ++ if (rw->counter != 0) { ++ __raw_spin_unlock(&rw->lock); ++ raw_local_irq_restore(flags); ++ ++ while (rw->counter != 0) ++ cpu_relax(); ++ ++ goto retry; ++ } ++ ++ rw->counter = -1; /* mark as write-locked */ ++ mb(); ++ raw_local_irq_restore(flags); ++} ++ ++static inline void __raw_write_unlock(raw_rwlock_t *rw) ++{ ++ rw->counter = 0; ++ __raw_spin_unlock(&rw->lock); ++} ++ ++/* Note that we have to ensure interrupts are disabled in case we're ++ * interrupted by some other code that wants to read_trylock() this lock */ ++static inline int __raw_write_trylock(raw_rwlock_t *rw) ++{ ++ unsigned long flags; ++ int result = 0; ++ ++ raw_local_irq_save(flags); ++ if (__raw_spin_trylock(&rw->lock)) { ++ if (rw->counter == 0) { ++ rw->counter = -1; ++ result = 1; ++ } else { ++ /* Read-locked. Oh well. */ ++ __raw_spin_unlock(&rw->lock); ++ } ++ } ++ raw_local_irq_restore(flags); ++ ++ return result; ++} ++ ++/* ++ * read_can_lock - would read_trylock() succeed? ++ * @lock: the rwlock in question. ++ */ ++static inline int __raw_read_can_lock(raw_rwlock_t *rw) ++{ ++ return rw->counter >= 0; ++} ++ ++/* ++ * write_can_lock - would write_trylock() succeed? ++ * @lock: the rwlock in question. ++ */ ++static inline int __raw_write_can_lock(raw_rwlock_t *rw) ++{ ++ return !rw->counter; ++} ++ ++#define _raw_spin_relax(lock) cpu_relax() ++#define _raw_read_relax(lock) cpu_relax() ++#define _raw_write_relax(lock) cpu_relax() ++ ++#endif /* _ASM_UBICOM32_SPINLOCK_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/spinlock_types.h +@@ -0,0 +1,43 @@ ++/* ++ * arch/ubicom32/include/asm/spinlock_types.h ++ * Spinlock related structure definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SPINLOCK_TYPES_H ++#define _ASM_UBICOM32_SPINLOCK_TYPES_H ++ ++typedef struct { ++ volatile unsigned int lock; ++} raw_spinlock_t; ++ ++typedef struct { ++ raw_spinlock_t lock; ++ volatile int counter; ++} raw_rwlock_t; ++ ++#define __RAW_SPIN_LOCK_UNLOCKED { 0 } ++#define __RAW_RW_LOCK_UNLOCKED { __RAW_SPIN_LOCK_UNLOCKED, 0 } ++ ++#endif /* _ASM_UBICOM32_SPINLOCK_TYPES_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/stacktrace.h +@@ -0,0 +1,72 @@ ++/* ++ * arch/ubicom32/include/asm/stacktrace.h ++ * Stacktrace functions for the Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_STACKTRACE_H ++#define _ASM_UBICOM32_STACKTRACE_H ++ ++#define between(a, b, c) (( \ ++ ((unsigned long) a) >= ((unsigned long) b)) && \ ++ (((unsigned long)a) <= ((unsigned long)c))) ++ ++/* ++ * These symbols are filled in by the linker. ++ */ ++extern unsigned long _stext; ++extern unsigned long _etext; ++ ++/* OCM text goes from __ocm_text_run_begin to __data_begin */ ++extern unsigned long __ocm_text_run_begin; ++extern unsigned long __data_begin; ++ ++/* Account for OCM case - see stacktrace.c maybe combine(also trap.c) */ ++/* ++ * ubicom32_is_kernel() ++ * ++ * Check to see if the given address belongs to the kernel. ++ * NOMMU does not permit any other means. ++ */ ++static inline int ubicom32_is_kernel(unsigned long addr) ++{ ++ int is_kernel = between(addr, &_stext, &_etext) || \ ++ between(addr, &__ocm_text_run_begin, &__data_begin); ++ ++#ifdef CONFIG_MODULES ++ if (!is_kernel) ++ is_kernel = is_module_address(addr); ++#endif ++ return is_kernel; ++} ++ ++extern unsigned long stacktrace_iterate( ++ unsigned long **trace, ++ unsigned long stext, unsigned long etext, ++ unsigned long ocm_stext, unsigned long ocm_etext, ++ unsigned long sstack, unsigned long estack); ++#ifdef CONFIG_STACKTRACE ++void stacktrace_save_entries(struct task_struct *tsk, struct stack_trace *trace, unsigned long sp); ++#endif ++#endif /* _ASM_UBICOM32_STACKTRACE_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/statfs.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/statfs.h ++ * Generic statfs.h definitions ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_STATFS_H ++#define _ASM_UBICOM32_STATFS_H ++ ++#include <asm-generic/statfs.h> ++ ++#endif /* _ASM_UBICOM32_STATFS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/stat.h +@@ -0,0 +1,104 @@ ++/* ++ * arch/ubicom32/include/asm/stat.h ++ * File status definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_STAT_H ++#define _ASM_UBICOM32_STAT_H ++ ++struct __old_kernel_stat { ++ unsigned short st_dev; ++ unsigned short st_ino; ++ unsigned short st_mode; ++ unsigned short st_nlink; ++ unsigned short st_uid; ++ unsigned short st_gid; ++ unsigned short st_rdev; ++ unsigned long st_size; ++ unsigned long st_atime; ++ unsigned long st_mtime; ++ unsigned long st_ctime; ++}; ++ ++struct stat { ++ unsigned short st_dev; ++ unsigned short __pad1; ++ unsigned long st_ino; ++ unsigned short st_mode; ++ unsigned short st_nlink; ++ unsigned short st_uid; ++ unsigned short st_gid; ++ unsigned short st_rdev; ++ unsigned short __pad2; ++ unsigned long st_size; ++ unsigned long st_blksize; ++ unsigned long st_blocks; ++ unsigned long st_atime; ++ unsigned long __unused1; ++ unsigned long st_mtime; ++ unsigned long __unused2; ++ unsigned long st_ctime; ++ unsigned long __unused3; ++ unsigned long __unused4; ++ unsigned long __unused5; ++}; ++ ++/* This matches struct stat64 in glibc2.1, hence the absolutely ++ * insane amounts of padding around dev_t's. ++ */ ++struct stat64 { ++ unsigned long long st_dev; ++ unsigned char __pad1[2]; ++ ++#define STAT64_HAS_BROKEN_ST_INO 1 ++ unsigned long __st_ino; ++ ++ unsigned int st_mode; ++ unsigned int st_nlink; ++ ++ unsigned long st_uid; ++ unsigned long st_gid; ++ ++ unsigned long long st_rdev; ++ unsigned char __pad3[2]; ++ ++ long long st_size; ++ unsigned long st_blksize; ++ ++ unsigned long long st_blocks; /* Number 512-byte blocks allocated. */ ++ ++ unsigned long st_atime; ++ unsigned long st_atime_nsec; ++ ++ unsigned long st_mtime; ++ unsigned long st_mtime_nsec; ++ ++ unsigned long st_ctime; ++ unsigned long st_ctime_nsec; ++ ++ unsigned long long st_ino; ++}; ++ ++#endif /* _ASM_UBICOM32_STAT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/string.h +@@ -0,0 +1,37 @@ ++/* ++ * arch/ubicom32/include/asm/string.h ++ * String operation definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_STRING_H ++#define _ASM_UBICOM32_STRING_H ++ ++#define __HAVE_ARCH_MEMSET ++extern void *memset(void *b, int c, size_t len); ++ ++#define __HAVE_ARCH_MEMCPY ++extern void *memcpy(void *to, const void *from, size_t len); ++ ++#endif /* _ASM_UBICOM32_STRING_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/switch-bcm539x.h +@@ -0,0 +1,57 @@ ++/* ++ * arch/ubicom32/include/asm/switch-bcm539x.h ++ * Broadcom bcm539x platform data definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SWITCH_BCM539X_H ++#define _ASM_UBICOM32_SWITCH_BCM539X_H ++ ++#define SWITCH_BCM539X_FLAG_HW_RESET 0x01 ++#define SWITCH_BCM539X_FLAG_SW_RESET 0x02 ++ ++struct switch_bcm539x_platform_data { ++ /* ++ * See flags above ++ */ ++ u32_t flags; ++ ++ /* ++ * GPIO to use for nReset ++ */ ++ int pin_reset; ++ ++ ++ /* ++ * GPIO to use for chip select ++ */ ++ int pin_cs; ++ ++ /* ++ * Name of this switch ++ */ ++ const char *name; ++}; ++ ++#endif /* _ASM_UBICOM32_SWITCH_BCM539X_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/system.h +@@ -0,0 +1,101 @@ ++/* ++ * arch/ubicom32/include/asm/system.h ++ * Low level switching definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_SYSTEM_H ++#define _ASM_UBICOM32_SYSTEM_H ++ ++#include <linux/irqflags.h> ++#include <linux/linkage.h> ++#include <asm/segment.h> ++#include <asm/entry.h> ++#include <asm/ldsr.h> ++#include <asm/irq.h> ++#include <asm/percpu.h> ++#include <asm/ubicom32-common.h> ++#include <asm/processor.h> ++ ++/* ++ * switch_to(n) should switch tasks to task ptr, first checking that ++ * ptr isn't the current task, in which case it does nothing. ++ */ ++asmlinkage void resume(void); ++extern void *__switch_to(struct task_struct *prev, ++ struct thread_struct *prev_switch, ++ struct thread_struct *next_switch); ++ ++/* ++ * We will need a per linux thread sw_ksp for the switch_to macro to ++ * track the kernel stack pointer for the current thread on that linux thread. ++ */ ++#define switch_to(prev,next,last) \ ++({ \ ++ void *_last; \ ++ _last = (void *) \ ++ __switch_to(prev, &prev->thread, &next->thread); \ ++ (last) = _last; \ ++}) ++ ++/* ++ * Force strict CPU ordering. ++ * Not really required on ubicom32... ++ */ ++#define nop() asm volatile ("nop"::) ++#define mb() asm volatile ("" : : :"memory") ++#define rmb() asm volatile ("" : : :"memory") ++#define wmb() asm volatile ("" : : :"memory") ++#define set_mb(var, value) ({ (var) = (value); wmb(); }) ++ ++#ifdef CONFIG_SMP ++#define smp_mb() mb() ++#define smp_rmb() rmb() ++#define smp_wmb() wmb() ++#define smp_read_barrier_depends() read_barrier_depends() ++#else ++#define smp_mb() mb() ++#define smp_rmb() rmb() ++#define smp_wmb() wmb() ++#define smp_read_barrier_depends() do { } while(0) ++#endif ++ ++#define read_barrier_depends() ((void)0) ++ ++/* ++ * The following defines change how the scheduler calls the switch_to() ++ * macro. ++ * ++ * 1) The first causes the runqueue to be unlocked on entry to ++ * switch_to(). Since our ctx code does not play with the runqueue ++ * we do not need it unlocked. ++ * ++ * 2) The later turns interrupts on during a ctxsw to reduce the latency of ++ * interrupts during ctx. At this point in the port, we believe that this ++ * latency is not a problem since we have very little code to perform a ctxsw. ++ */ ++// #define __ARCH_WANT_UNLOCKED_CTXSW ++// #define __ARCH_WANT_INTERRUPTS_ON_CTXSW ++ ++#endif /* _ASM_UBICOM32_SYSTEM_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/termbits.h +@@ -0,0 +1,227 @@ ++/* ++ * arch/ubicom32/include/asm/termbits.h ++ * Terminal/serial port definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_TERMBITS_H ++#define _ASM_UBICOM32_TERMBITS_H ++ ++#include <linux/posix_types.h> ++ ++typedef unsigned char cc_t; ++typedef unsigned int speed_t; ++typedef unsigned int tcflag_t; ++ ++#define NCCS 19 ++struct termios { ++ tcflag_t c_iflag; /* input mode flags */ ++ tcflag_t c_oflag; /* output mode flags */ ++ tcflag_t c_cflag; /* control mode flags */ ++ tcflag_t c_lflag; /* local mode flags */ ++ cc_t c_line; /* line discipline */ ++ cc_t c_cc[NCCS]; /* control characters */ ++}; ++ ++struct termios2 { ++ tcflag_t c_iflag; /* input mode flags */ ++ tcflag_t c_oflag; /* output mode flags */ ++ tcflag_t c_cflag; /* control mode flags */ ++ tcflag_t c_lflag; /* local mode flags */ ++ cc_t c_line; /* line discipline */ ++ cc_t c_cc[NCCS]; /* control characters */ ++ speed_t c_ispeed; /* input speed */ ++ speed_t c_ospeed; /* output speed */ ++}; ++ ++struct ktermios { ++ tcflag_t c_iflag; /* input mode flags */ ++ tcflag_t c_oflag; /* output mode flags */ ++ tcflag_t c_cflag; /* control mode flags */ ++ tcflag_t c_lflag; /* local mode flags */ ++ cc_t c_line; /* line discipline */ ++ cc_t c_cc[NCCS]; /* control characters */ ++ speed_t c_ispeed; /* input speed */ ++ speed_t c_ospeed; /* output speed */ ++}; ++ ++/* c_cc characters */ ++#define VINTR 0 ++#define VQUIT 1 ++#define VERASE 2 ++#define VKILL 3 ++#define VEOF 4 ++#define VTIME 5 ++#define VMIN 6 ++#define VSWTC 7 ++#define VSTART 8 ++#define VSTOP 9 ++#define VSUSP 10 ++#define VEOL 11 ++#define VREPRINT 12 ++#define VDISCARD 13 ++#define VWERASE 14 ++#define VLNEXT 15 ++#define VEOL2 16 ++ ++ ++/* c_iflag bits */ ++#define IGNBRK 0000001 ++#define BRKINT 0000002 ++#define IGNPAR 0000004 ++#define PARMRK 0000010 ++#define INPCK 0000020 ++#define ISTRIP 0000040 ++#define INLCR 0000100 ++#define IGNCR 0000200 ++#define ICRNL 0000400 ++#define IUCLC 0001000 ++#define IXON 0002000 ++#define IXANY 0004000 ++#define IXOFF 0010000 ++#define IMAXBEL 0020000 ++#define IUTF8 0040000 ++ ++/* c_oflag bits */ ++#define OPOST 0000001 ++#define OLCUC 0000002 ++#define ONLCR 0000004 ++#define OCRNL 0000010 ++#define ONOCR 0000020 ++#define ONLRET 0000040 ++#define OFILL 0000100 ++#define OFDEL 0000200 ++#define NLDLY 0000400 ++#define NL0 0000000 ++#define NL1 0000400 ++#define CRDLY 0003000 ++#define CR0 0000000 ++#define CR1 0001000 ++#define CR2 0002000 ++#define CR3 0003000 ++#define TABDLY 0014000 ++#define TAB0 0000000 ++#define TAB1 0004000 ++#define TAB2 0010000 ++#define TAB3 0014000 ++#define XTABS 0014000 ++#define BSDLY 0020000 ++#define BS0 0000000 ++#define BS1 0020000 ++#define VTDLY 0040000 ++#define VT0 0000000 ++#define VT1 0040000 ++#define FFDLY 0100000 ++#define FF0 0000000 ++#define FF1 0100000 ++ ++/* c_cflag bit meaning */ ++#define CBAUD 0010017 ++#define B0 0000000 /* hang up */ ++#define B50 0000001 ++#define B75 0000002 ++#define B110 0000003 ++#define B134 0000004 ++#define B150 0000005 ++#define B200 0000006 ++#define B300 0000007 ++#define B600 0000010 ++#define B1200 0000011 ++#define B1800 0000012 ++#define B2400 0000013 ++#define B4800 0000014 ++#define B9600 0000015 ++#define B19200 0000016 ++#define B38400 0000017 ++#define EXTA B19200 ++#define EXTB B38400 ++#define CSIZE 0000060 ++#define CS5 0000000 ++#define CS6 0000020 ++#define CS7 0000040 ++#define CS8 0000060 ++#define CSTOPB 0000100 ++#define CREAD 0000200 ++#define PARENB 0000400 ++#define PARODD 0001000 ++#define HUPCL 0002000 ++#define CLOCAL 0004000 ++#define CBAUDEX 0010000 ++#define BOTHER 0010000 ++#define B57600 0010001 ++#define B115200 0010002 ++#define B230400 0010003 ++#define B460800 0010004 ++#define B500000 0010005 ++#define B576000 0010006 ++#define B921600 0010007 ++#define B1000000 0010010 ++#define B1152000 0010011 ++#define B1500000 0010012 ++#define B2000000 0010013 ++#define B2500000 0010014 ++#define B3000000 0010015 ++#define B3500000 0010016 ++#define B4000000 0010017 ++#define CIBAUD 002003600000 /* input baud rate */ ++#define CMSPAR 010000000000 /* mark or space (stick) parity */ ++#define CRTSCTS 020000000000 /* flow control */ ++ ++#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */ ++ ++/* c_lflag bits */ ++#define ISIG 0000001 ++#define ICANON 0000002 ++#define XCASE 0000004 ++#define ECHO 0000010 ++#define ECHOE 0000020 ++#define ECHOK 0000040 ++#define ECHONL 0000100 ++#define NOFLSH 0000200 ++#define TOSTOP 0000400 ++#define ECHOCTL 0001000 ++#define ECHOPRT 0002000 ++#define ECHOKE 0004000 ++#define FLUSHO 0010000 ++#define PENDIN 0040000 ++#define IEXTEN 0100000 ++ ++ ++/* tcflow() and TCXONC use these */ ++#define TCOOFF 0 ++#define TCOON 1 ++#define TCIOFF 2 ++#define TCION 3 ++ ++/* tcflush() and TCFLSH use these */ ++#define TCIFLUSH 0 ++#define TCOFLUSH 1 ++#define TCIOFLUSH 2 ++ ++/* tcsetattr uses these */ ++#define TCSANOW 0 ++#define TCSADRAIN 1 ++#define TCSAFLUSH 2 ++ ++#endif /* _ASM_UBICOM32_TERMBITS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/termios.h +@@ -0,0 +1,119 @@ ++/* ++ * arch/ubicom32/include/asm/termios.h ++ * Ubicom32 termio definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_TERMIOS_H ++#define _ASM_UBICOM32_TERMIOS_H ++ ++#include <asm/termbits.h> ++#include <asm/ioctls.h> ++ ++struct winsize { ++ unsigned short ws_row; ++ unsigned short ws_col; ++ unsigned short ws_xpixel; ++ unsigned short ws_ypixel; ++}; ++ ++#define NCC 8 ++struct termio { ++ unsigned short c_iflag; /* input mode flags */ ++ unsigned short c_oflag; /* output mode flags */ ++ unsigned short c_cflag; /* control mode flags */ ++ unsigned short c_lflag; /* local mode flags */ ++ unsigned char c_line; /* line discipline */ ++ unsigned char c_cc[NCC]; /* control characters */ ++}; ++ ++#ifdef __KERNEL__ ++/* intr=^C quit=^| erase=del kill=^U ++ eof=^D vtime=\0 vmin=\1 sxtc=\0 ++ start=^Q stop=^S susp=^Z eol=\0 ++ reprint=^R discard=^U werase=^W lnext=^V ++ eol2=\0 ++*/ ++#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" ++#endif ++ ++/* modem lines */ ++#define TIOCM_LE 0x001 ++#define TIOCM_DTR 0x002 ++#define TIOCM_RTS 0x004 ++#define TIOCM_ST 0x008 ++#define TIOCM_SR 0x010 ++#define TIOCM_CTS 0x020 ++#define TIOCM_CAR 0x040 ++#define TIOCM_RNG 0x080 ++#define TIOCM_DSR 0x100 ++#define TIOCM_CD TIOCM_CAR ++#define TIOCM_RI TIOCM_RNG ++#define TIOCM_OUT1 0x2000 ++#define TIOCM_OUT2 0x4000 ++#define TIOCM_LOOP 0x8000 ++ ++/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ ++ ++#ifdef __KERNEL__ ++ ++/* ++ * Translate a "termio" structure into a "termios". Ugh. ++ */ ++#define user_termio_to_kernel_termios(termios, termio) \ ++({ \ ++ unsigned short tmp; \ ++ get_user(tmp, &(termio)->c_iflag); \ ++ (termios)->c_iflag = (0xffff0000 & ((termios)->c_iflag)) | tmp; \ ++ get_user(tmp, &(termio)->c_oflag); \ ++ (termios)->c_oflag = (0xffff0000 & ((termios)->c_oflag)) | tmp; \ ++ get_user(tmp, &(termio)->c_cflag); \ ++ (termios)->c_cflag = (0xffff0000 & ((termios)->c_cflag)) | tmp; \ ++ get_user(tmp, &(termio)->c_lflag); \ ++ (termios)->c_lflag = (0xffff0000 & ((termios)->c_lflag)) | tmp; \ ++ get_user((termios)->c_line, &(termio)->c_line); \ ++ copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ ++}) ++ ++/* ++ * Translate a "termios" structure into a "termio". Ugh. ++ */ ++#define kernel_termios_to_user_termio(termio, termios) \ ++({ \ ++ put_user((termios)->c_iflag, &(termio)->c_iflag); \ ++ put_user((termios)->c_oflag, &(termio)->c_oflag); \ ++ put_user((termios)->c_cflag, &(termio)->c_cflag); \ ++ put_user((termios)->c_lflag, &(termio)->c_lflag); \ ++ put_user((termios)->c_line, &(termio)->c_line); \ ++ copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ ++}) ++ ++#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios2)) ++#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios2)) ++#define user_termios_to_kernel_termios_1(k, u) copy_from_user(k, u, sizeof(struct termios)) ++#define kernel_termios_to_user_termios_1(u, k) copy_to_user(u, k, sizeof(struct termios)) ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _ASM_UBICOM32_TERMIOS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/thread-asm.h +@@ -0,0 +1,49 @@ ++/* ++ * arch/ubicom32/include/asm/thread-asm.h ++ * Ubicom32 architecture specific thread definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_THREAD_ASM_H ++#define _ASM_UBICOM32_THREAD_ASM_H ++ ++/* ++ * thread_get_self ++ * Read and shift the current thread into reg ++ */ ++.macro thread_get_self reg ++ lsr.4 \reg, ROSR, #2 ++ and.4 \reg, #31, \reg /* Mask to get thread number into register */ ++.endm ++ ++/* ++ * thread_get_self_mask ++ * Read and shift the current thread mask into reg ++ */ ++.macro thread_get_self_mask reg ++ lsr.4 \reg, ROSR, #2 ++ lsl.4 \reg, #1, \reg /* Thread bit */ ++.endm ++ ++#endif /* _ASM_UBICOM32_THREAD_ASM_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/thread.h +@@ -0,0 +1,313 @@ ++/* ++ * arch/ubicom32/include/asm/thread.h ++ * Ubicom32 architecture specific thread definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_THREAD_H ++#define _ASM_UBICOM32_THREAD_H ++ ++#if !defined(__ASSEMBLY__) ++ ++#include <asm/ptrace.h> ++#include <asm/ubicom32-common.h> ++ ++typedef int thread_t; ++typedef unsigned char thread_type_t; ++typedef void (*thread_exec_fn_t)(void *arg); ++ ++#define THREAD_NULL 0x40 ++#define THREAD_TYPE_HRT (1 << 0) ++#define THREAD_TYPE_SPECIAL 0 ++#define THREAD_TYPE_NORMAL 0 ++#define THREAD_TYPE_BACKGROUND (1 << 1) ++ ++/* ++ * This is the upper bound on the maximum hardware threads that one will find ++ * on a Ubicom processor. It is used to size per hardware thread data structures. ++ */ ++#define THREAD_ARCHITECTURAL_MAX 16 ++ ++/* ++ * TODO: Rename this at some point to be thread_ ++ */ ++extern unsigned int sw_ksp[THREAD_ARCHITECTURAL_MAX]; ++ ++ ++/* ++ * thread_get_self() ++ */ ++static inline thread_t thread_get_self(void) ++{ ++ thread_t result; ++ asm ( ++ "lsr.4 %0, ROSR, #2 \n\t" ++ : "=d" (result) ++ : ++ : "cc" ++ ); ++ return result & 31; ++} ++ ++/* ++ * thread_suspend() ++ */ ++static inline void thread_suspend(void) ++{ ++ asm volatile ( ++ "suspend\n\t" ++ : ++ : ++ ); ++} ++ ++/* ++ * thread_resume() ++ */ ++static inline void thread_resume(thread_t thread) ++{ ++ asm volatile ( ++ "move.4 MT_ACTIVE_SET, %0 \n\t" ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ : ++ : "d" (1 << thread) ++ ); ++} ++ ++ ++ ++/* ++ * thread_enable_mask() ++ * Enable all threads in the mask. ++ * ++ * All writes to MT_EN must be protected by the MT_EN_LOCK bit ++ */ ++static inline void thread_enable_mask(unsigned int mask) ++{ ++ /* ++ * must flush the pipeline twice. ++ * first pipe_flush is to ensure write to MT_EN is completed ++ * second one is to ensure any new instructions from ++ * the targeted thread (the one being disabled), that ++ * are issued while the write to MT_EN is being executed, ++ * are completed. ++ */ ++ UBICOM32_LOCK(MT_EN_LOCK_BIT); ++ asm volatile ( ++ "or.4 MT_EN, MT_EN, %0 \n\t" ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ : ++ : "d" (mask) ++ : "cc" ++ ); ++ UBICOM32_UNLOCK(MT_EN_LOCK_BIT); ++} ++ ++/* ++ * thread_enable() ++ */ ++static inline void thread_enable(thread_t thread) ++{ ++ thread_enable_mask(1 << thread); ++} ++ ++/* ++ * thread_disable_mask() ++ * Disable all threads in the mask. ++ * ++ * All writes to MT_EN must be protected by the MT_EN_LOCK bit ++ */ ++static inline void thread_disable_mask(unsigned int mask) ++{ ++ /* ++ * must flush the pipeline twice. ++ * first pipe_flush is to ensure write to MT_EN is completed ++ * second one is to ensure any new instructions from ++ * the targeted thread (the one being disabled), that ++ * are issued while the write to MT_EN is being executed, ++ * are completed. ++ */ ++ UBICOM32_LOCK(MT_EN_LOCK_BIT); ++ asm volatile ( ++ "and.4 MT_EN, MT_EN, %0 \n\t" ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ : ++ : "d" (~mask) ++ : "cc" ++ ); ++ UBICOM32_UNLOCK(MT_EN_LOCK_BIT); ++} ++ ++/* ++ * thread_disable() ++ */ ++static inline void thread_disable(thread_t thread) ++{ ++ thread_disable_mask(1 << thread); ++} ++ ++/* ++ * thread_disable_others() ++ * Disable all other threads ++ */ ++static inline void thread_disable_others(void) ++{ ++ thread_t self = thread_get_self(); ++ thread_disable_mask(~(1 << self)); ++} ++ ++/* ++ * thread_is_trapped() ++ * Is the specified tid trapped? ++ */ ++static inline int thread_is_trapped(thread_t tid) ++{ ++ int thread_mask = (1 << tid); ++ int trap_thread; ++ ++ asm ( ++ "move.4 %0, MT_TRAP \n\t" ++ : "=d" (trap_thread) ++ : ++ ); ++ return (trap_thread & thread_mask); ++} ++ ++/* ++ * thread_is_enabled() ++ * Is the specified tid enabled? ++ */ ++static inline int thread_is_enabled(thread_t tid) ++{ ++ int thread_mask = (1 << tid); ++ int enabled_threads; ++ ++ asm ( ++ "move.4 %0, MT_EN \n\t" ++ : "=d" (enabled_threads) ++ : ++ ); ++ return (enabled_threads & thread_mask); ++} ++ ++/* ++ * thread_get_instruction_count() ++ */ ++static inline unsigned int thread_get_instruction_count(void) ++{ ++ unsigned int result; ++ asm ( ++ "move.4 %0, INST_CNT \n\t" ++ : "=r" (result) ++ ); ++ return result; ++} ++ ++/* ++ * thread_get_pc() ++ * pc could point to a speculative and cancelled instruction unless thread is disabled ++ */ ++static inline void *thread_get_pc(thread_t thread) ++{ ++ void *result; ++ asm ( ++ "move.4 csr, %1 \n\t" ++ "setcsr_flush 0 \n\t" ++ "move.4 %0, pc \n\t" ++ "move.4 csr, #0 \n\t" ++ "setcsr_flush 0 \n\t" ++ : "=r" (result) ++ : "r" ((thread << 9) | (1 << 8)) ++ ); ++ return result; ++} ++ ++/* ++ * thread_get_trap_cause() ++ * This should be called only when the thread is not running ++ */ ++static inline unsigned int thread_get_trap_cause(thread_t thread) ++{ ++ unsigned int result; ++ asm ( ++ "move.4 csr, %1 \n\t" ++ "setcsr_flush 0 \n\t" ++ "move.4 %0, trap_cause \n\t" ++ "move.4 csr, #0 \n\t" ++ "setcsr_flush 0 \n\t" ++ : "=r" (result) ++ : "r" ((thread << 9) | (1 << 8)) ++ ); ++ return result; ++} ++ ++/* ++ * THREAD_STALL macro. ++ */ ++#define THREAD_STALL \ ++ asm volatile ( \ ++ "move.4 mt_dbg_active_clr, #-1 \n\t" \ ++ "pipe_flush 0 \n\t" \ ++ : \ ++ : \ ++ ) ++ ++extern unsigned int thread_get_mainline(void); ++extern void thread_set_mainline(thread_t tid); ++extern thread_t thread_alloc(void); ++extern thread_t thread_start(thread_t thread, thread_exec_fn_t exec, void *arg, unsigned int *sp_high, thread_type_t type); ++ ++/* ++ * asm macros ++ */ ++asm ( ++/* ++ * thread_get_self ++ * Read and shift the current thread into reg ++ */ ++".macro thread_get_self reg \n\t" ++" lsr.4 \\reg, ROSR, #2 \n\t" ++" and.4 \\reg, #31, \\reg \n\t"/* Mask to get thread number into ++ * register */ ++".endm \n\t" ++ ++/* ++ * thread_get_self_mask ++ * Read and shift the current thread mask into reg ++ */ ++".macro thread_get_self_mask reg \n\t" ++" lsr.4 \\reg, ROSR, #2 \n\t" ++" lsl.4 \\reg, #1, \\reg \n\t" /* Thread bit */ ++".endm \n\t" ++ ); ++ ++#else /* __ASSEMBLY__ */ ++ ++#include <asm/thread-asm.h> ++ ++#endif /* __ASSEMBLY__ */ ++#endif /* _ASM_UBICOM32_THREAD_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/thread_info.h +@@ -0,0 +1,134 @@ ++/* ++ * arch/ubicom32/include/asm/thread_info.h ++ * Ubicom32 architecture low-level thread information. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Adapted from the i386 and PPC versions by Greg Ungerer (gerg@snapgear.com) ++ * Copyright (C) 2002 David Howells (dhowells@redhat.com) ++ * - Incorporating suggestions made by Linus Torvalds and Dave Miller ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_THREAD_INFO_H ++#define _ASM_UBICOM32_THREAD_INFO_H ++ ++#include <asm/page.h> ++ ++/* ++ * Size of kernel stack for each process. This must be a power of 2... ++ */ ++#ifdef CONFIG_4KSTACKS ++#define THREAD_SIZE_ORDER (0) ++#else ++#define THREAD_SIZE_ORDER (1) ++#endif ++ ++/* ++ * for asm files, THREAD_SIZE is now generated by asm-offsets.c ++ */ ++#define THREAD_SIZE (PAGE_SIZE<<THREAD_SIZE_ORDER) ++ ++#ifdef __KERNEL__ ++ ++#ifndef __ASSEMBLY__ ++ ++/* ++ * low level task data. ++ */ ++struct thread_info { ++ struct task_struct *task; /* main task structure */ ++ struct exec_domain *exec_domain; /* execution domain */ ++ unsigned long flags; /* low level flags */ ++ int cpu; /* cpu we're on */ ++ int preempt_count; /* 0 => preemptable, <0 => BUG */ ++ int interrupt_nesting; /* Interrupt nesting level. */ ++ struct restart_block restart_block; ++}; ++ ++/* ++ * macros/functions for gaining access to the thread information structure ++ */ ++#define INIT_THREAD_INFO(tsk) \ ++{ \ ++ .task = &tsk, \ ++ .exec_domain = &default_exec_domain, \ ++ .flags = 0, \ ++ .cpu = 0, \ ++ .interrupt_nesting = 0, \ ++ .restart_block = { \ ++ .fn = do_no_restart_syscall, \ ++ }, \ ++} ++ ++#define init_thread_info (init_thread_union.thread_info) ++#define init_stack (init_thread_union.stack) ++ ++ ++/* how to get the thread information struct from C */ ++static inline struct thread_info *current_thread_info(void) ++{ ++ struct thread_info *ti; ++ ++ asm ( ++ "and.4 %0, sp, %1\n\t" ++ : "=&r" (ti) ++ : "d" (~(THREAD_SIZE-1)) ++ : "cc" ++ ); ++ ++ return ti; ++} ++ ++#define STACK_WARN (THREAD_SIZE / 8) ++ ++#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR 1 ++ ++/* thread information allocation */ ++#define alloc_thread_info(tsk) ((struct thread_info *) \ ++ __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER)) ++#define free_thread_info(ti) free_pages((unsigned long) (ti), THREAD_SIZE_ORDER) ++#endif /* __ASSEMBLY__ */ ++ ++#define PREEMPT_ACTIVE 0x4000000 ++ ++/* ++ * thread information flag bit numbers ++ */ ++#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ ++#define TIF_SIGPENDING 1 /* signal pending */ ++#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ ++#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling ++ TIF_NEED_RESCHED */ ++#define TIF_MEMDIE 4 ++ ++/* as above, but as bit values */ ++#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) ++#define _TIF_SIGPENDING (1<<TIF_SIGPENDING) ++#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) ++#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) ++ ++#define _TIF_WORK_MASK 0x0000FFFE /* work to do on interrupt/exception return */ ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _ASM_UBICOM32_THREAD_INFO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/timex.h +@@ -0,0 +1,56 @@ ++/* ++ * arch/ubicom32/include/asm/timex.h ++ * Ubicom32 architecture timex specifications. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_TIMEX_H ++#define _ASM_UBICOM32_TIMEX_H ++ ++#define CLOCK_TICK_RATE 266000000 ++ ++// #define ARCH_HAS_READ_CURRENT_TIMER ++ ++typedef unsigned long cycles_t; ++ ++static inline cycles_t get_cycles(void) ++{ ++ return 0; ++} ++ ++extern int timer_alloc(void); ++extern void timer_set(int timervector, unsigned int cycles); ++extern int timer_reset(int timervector, unsigned int cycles); ++extern void timer_tick_init(void); ++extern void timer_device_init(void); ++ ++#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) ++extern void local_timer_interrupt(void); ++#endif ++ ++#if defined(CONFIG_LOCAL_TIMERS) || defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) ++extern int local_timer_setup(unsigned int cpu); ++#endif ++ ++#endif /* _ASM_UBICOM32_TIMEX_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/tlbflush.h +@@ -0,0 +1,79 @@ ++/* ++ * arch/ubicom32/include/asm/tlbflush.h ++ * TLB operations for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2000 Lineo, David McCullough <davidm@uclinux.org> ++ * Copyright (C) 2000-2002, Greg Ungerer <gerg@snapgear.com> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_TLB_FLUSH_H ++#define _ASM_UBICOM32_TLB_FLUSH_H ++ ++#include <asm/setup.h> ++ ++/* ++ * flush all user-space atc entries. ++ */ ++static inline void __flush_tlb(void) ++{ ++ BUG(); ++} ++ ++static inline void __flush_tlb_one(unsigned long addr) ++{ ++ BUG(); ++} ++ ++#define flush_tlb() __flush_tlb() ++ ++/* ++ * flush all atc entries (both kernel and user-space entries). ++ */ ++static inline void flush_tlb_all(void) ++{ ++ BUG(); ++} ++ ++static inline void flush_tlb_mm(struct mm_struct *mm) ++{ ++ BUG(); ++} ++ ++static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) ++{ ++ BUG(); ++} ++ ++static inline void flush_tlb_range(struct mm_struct *mm, ++ unsigned long start, unsigned long end) ++{ ++ BUG(); ++} ++ ++static inline void flush_tlb_kernel_page(unsigned long addr) ++{ ++ BUG(); ++} ++ ++#endif /* _ASM_UBICOM32_TLB_FLUSH_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/tlb.h +@@ -0,0 +1,47 @@ ++/* ++ * arch/ubicom32/include/asm/tlb.h ++ * Ubicom32 architecture TLB operations. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_TLB_H ++#define _ASM_UBICOM32_TLB_H ++ ++/* ++ * ubicom32 doesn't need any special per-pte or ++ * per-vma handling.. ++ */ ++#define tlb_start_vma(tlb, vma) do { } while (0) ++#define tlb_end_vma(tlb, vma) do { } while (0) ++#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0) ++ ++/* ++ * .. because we flush the whole mm when it ++ * fills up. ++ */ ++#define tlb_flush(tlb) ++ ++#include <asm-generic/tlb.h> ++ ++#endif /* _ASM_UBICOM32_TLB_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/topology.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/topology.h ++ * Generic topology.h definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_TOPOLOGY_H ++#define _ASM_UBICOM32_TOPOLOGY_H ++ ++#include <asm-generic/topology.h> ++ ++#endif /* _ASM_UBICOM32_TOPOLOGY_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/traps.h +@@ -0,0 +1,55 @@ ++/* ++ * arch/ubicom32/include/asm/traps.h ++ * Trap related definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_TRAPS_H ++#define _ASM_UBICOM32_TRAPS_H ++ ++/* ++ * Trap causes passed from ultra to Host OS ++ */ ++#define TRAP_CAUSE_TOTAL 13 ++#define TRAP_CAUSE_DST_RANGE_ERR 12 ++#define TRAP_CAUSE_SRC1_RANGE_ERR 11 ++#define TRAP_CAUSE_I_RANGE_ERR 10 ++#define TRAP_CAUSE_DCAPT 9 ++#define TRAP_CAUSE_DST_SERROR 8 ++#define TRAP_CAUSE_SRC1_SERROR 7 ++#define TRAP_CAUSE_DST_MISALIGNED 6 ++#define TRAP_CAUSE_SRC1_MISALIGNED 5 ++#define TRAP_CAUSE_DST_DECODE_ERR 4 ++#define TRAP_CAUSE_SRC1_DECODE_ERR 3 ++#define TRAP_CAUSE_ILLEGAL_INST 2 ++#define TRAP_CAUSE_I_SERROR 1 ++#define TRAP_CAUSE_I_DECODE_ERR 0 ++ ++extern void trap_handler(int irq, struct pt_regs *regs); ++extern void trap_init_interrupt(void); ++extern void unaligned_emulate(unsigned int thread); ++extern int unaligned_only(unsigned int cause); ++ ++#endif /* _ASM_UBICOM32_TRAPS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/types.h +@@ -0,0 +1,75 @@ ++/* ++ * arch/ubicom32/include/asm/types.h ++ * Date type definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_TYPES_H ++#define _ASM_UBICOM32_TYPES_H ++ ++/* ++ * This file is never included by application software unless ++ * explicitly requested (e.g., via linux/types.h) in which case the ++ * application is Linux specific so (user-) name space pollution is ++ * not a major issue. However, for interoperability, libraries still ++ * need to be careful to avoid a name clashes. ++ */ ++ ++#include <asm-generic/int-ll64.h> ++ ++#ifndef __ASSEMBLY__ ++ ++typedef unsigned short umode_t; ++ ++#endif /* __ASSEMBLY__ */ ++ ++/* ++ * These aren't exported outside the kernel to avoid name space clashes ++ */ ++#ifdef __KERNEL__ ++ ++#define BITS_PER_LONG 32 ++ ++#ifndef __ASSEMBLY__ ++ ++/* DMA addresses are always 32-bits wide */ ++ ++typedef u32 dma_addr_t; ++typedef u32 dma64_addr_t; ++ ++/* ++ * XXX These are "Ubicom style" typedefs. They should be removed in all files used by linux. ++ */ ++typedef u32 u32_t; ++typedef s32 s32_t; ++typedef u16 u16_t; ++typedef s16 s16_t; ++typedef u8 u8_t; ++typedef s8 s8_t; ++ ++#endif /* __ASSEMBLY__ */ ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _ASM_UBICOM32_TYPES_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/uaccess.h +@@ -0,0 +1,347 @@ ++/* ++ * arch/ubicom32/include/asm/uaccess.h ++ * User space memory access functions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * arch/alpha ++ */ ++#ifndef _ASM_UBICOM32_UACCESS_H ++#define _ASM_UBICOM32_UACCESS_H ++ ++/* ++ * User space memory access functions ++ */ ++#include <linux/sched.h> ++#include <linux/mm.h> ++#include <linux/string.h> ++ ++#include <asm/segment.h> ++ ++#define VERIFY_READ 0 ++#define VERIFY_WRITE 1 ++ ++/* ++ * The exception table consists of pairs of addresses: the first is the ++ * address of an instruction that is allowed to fault, and the second is ++ * the address at which the program should continue. No registers are ++ * modified, so it is entirely up to the continuation code to figure out ++ * what to do. ++ * ++ * All the routines below use bits of fixup code that are out of line ++ * with the main instruction path. This means when everything is well, ++ * we don't even have to jump over them. Further, they do not intrude ++ * on our cache or tlb entries. ++ */ ++struct exception_table_entry ++{ ++ unsigned long insn, fixup; ++}; ++ ++/* ++ * Ubicom32 does not currently support the exception table handling. ++ */ ++extern unsigned long search_exception_table(unsigned long); ++ ++ ++#if defined(CONFIG_ACCESS_OK_CHECKS_ENABLED) ++extern int __access_ok(unsigned long addr, unsigned long size); ++#else ++static inline int __access_ok(unsigned long addr, unsigned long size) ++{ ++ return 1; ++} ++#endif ++#define access_ok(type, addr, size) \ ++ likely(__access_ok((unsigned long)(addr), (size))) ++ ++/* ++ * The following functions do not exist. They keep callers ++ * of put_user and get_user from passing unsupported argument ++ * types. They result in a link time error. ++ */ ++extern int __put_user_bad(void); ++extern int __get_user_bad(void); ++ ++/* ++ * __put_user_no_check() ++ * Put the requested data into the user space verifying the address ++ * ++ * Careful to not ++ * (a) re-use the arguments for side effects (sizeof/typeof is ok) ++ * (b) require any knowledge of processes at this stage ++ */ ++#define __put_user_no_check(x, ptr, size) \ ++({ \ ++ int __pu_err = 0; \ ++ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ ++ switch (size) { \ ++ case 1: \ ++ case 2: \ ++ case 4: \ ++ case 8: \ ++ *__pu_addr = (__typeof__(*(ptr)))x; \ ++ break; \ ++ default: \ ++ __pu_err = __put_user_bad(); \ ++ break; \ ++ } \ ++ __pu_err; \ ++}) ++ ++/* ++ * __put_user_check() ++ * Put the requested data into the user space verifying the address ++ * ++ * Careful to not ++ * (a) re-use the arguments for side effects (sizeof/typeof is ok) ++ * (b) require any knowledge of processes at this stage ++ * ++ * If requested, access_ok() will verify that ptr is a valid user ++ * pointer. ++ */ ++#define __put_user_check(x, ptr, size) \ ++({ \ ++ int __pu_err = -EFAULT; \ ++ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ ++ if (access_ok(VERIFY_WRITE, __pu_addr, size)) { \ ++ __pu_err = 0; \ ++ switch (size) { \ ++ case 1: \ ++ case 2: \ ++ case 4: \ ++ case 8: \ ++ *__pu_addr = (__typeof__(*(ptr)))x; \ ++ break; \ ++ default: \ ++ __pu_err = __put_user_bad(); \ ++ break; \ ++ } \ ++ } \ ++ __pu_err; \ ++}) ++ ++/* ++ * __get_user_no_check() ++ * Read the value at ptr into x. ++ * ++ * If requested, access_ok() will verify that ptr is a valid user ++ * pointer. If the caller passes a modifying argument for ptr (e.g. x++) ++ * this macro will not work. ++ */ ++#define __get_user_no_check(x, ptr, size) \ ++({ \ ++ int __gu_err = 0; \ ++ __typeof__((x)) __gu_val = 0; \ ++ const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ ++ switch (size) { \ ++ case 1: \ ++ case 2: \ ++ case 4: \ ++ case 8: \ ++ __gu_val = (__typeof__((x)))*(__gu_addr); \ ++ break; \ ++ default: \ ++ __gu_err = __get_user_bad(); \ ++ (x) = 0; \ ++ break; \ ++ } \ ++ (x) = __gu_val; \ ++ __gu_err; \ ++}) ++ ++/* ++ * __get_user_check() ++ * Read the value at ptr into x. ++ * ++ * If requested, access_ok() will verify that ptr is a valid user ++ * pointer. ++ */ ++#define __get_user_check(x, ptr, size) \ ++({ \ ++ int __gu_err = -EFAULT; \ ++ __typeof__(x) __gu_val = 0; \ ++ const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ ++ if (access_ok(VERIFY_READ, __gu_addr, size)) { \ ++ __gu_err = 0; \ ++ switch (size) { \ ++ case 1: \ ++ case 2: \ ++ case 4: \ ++ case 8: \ ++ __gu_val = (__typeof__((x)))*(__gu_addr); \ ++ break; \ ++ default: \ ++ __gu_err = __get_user_bad(); \ ++ (x) = 0; \ ++ break; \ ++ } \ ++ } \ ++ (x) = __gu_val; \ ++ __gu_err; \ ++}) ++ ++/* ++ * The "xxx" versions are allowed to perform some amount of address ++ * space checking. See access_ok(). ++ */ ++#define put_user(x,ptr) \ ++ __put_user_check((__typeof__(*(ptr)))(x),(ptr), sizeof(*(ptr))) ++#define get_user(x,ptr) \ ++ __get_user_check((x), (ptr), sizeof(*(ptr))) ++ ++/* ++ * The "__xxx" versions do not do address space checking, useful when ++ * doing multiple accesses to the same area (the programmer has to do the ++ * checks by hand with "access_ok()") ++ */ ++#define __put_user(x,ptr) \ ++ __put_user_no_check((__typeof__(*(ptr)))(x),(ptr), sizeof(*(ptr))) ++#define __get_user(x,ptr) \ ++ __get_user_no_check((x), (ptr), sizeof(*(ptr))) ++ ++/* ++ * __copy_tofrom_user_no_check() ++ * Copy the data either to or from user space. ++ * ++ * Return the number of bytes NOT copied. ++ */ ++static inline unsigned long ++__copy_tofrom_user_no_check(void *to, const void *from, unsigned long n) ++{ ++ memcpy(to, from, n); ++ return 0; ++} ++ ++/* ++ * copy_to_user() ++ * Copy the kernel data to user space. ++ * ++ * Return the number of bytes that were copied. ++ */ ++static inline unsigned long ++copy_to_user(void __user *to, const void *from, unsigned long n) ++{ ++ if (!access_ok(VERIFY_WRITE, to, n)) { ++ return n; ++ } ++ return __copy_tofrom_user_no_check((__force void *)to, from, n); ++} ++ ++/* ++ * copy_from_user() ++ * Copy the user data to kernel space. ++ * ++ * Return the number of bytes that were copied. On error, we zero ++ * out the destination. ++ */ ++static inline unsigned long ++copy_from_user(void *to, const void __user *from, unsigned long n) ++{ ++ if (!access_ok(VERIFY_READ, from, n)) { ++ return n; ++ } ++ return __copy_tofrom_user_no_check(to, (__force void *)from, n); ++} ++ ++#define __copy_to_user(to, from, n) \ ++ __copy_tofrom_user_no_check((__force void *)to, from, n) ++#define __copy_from_user(to, from, n) \ ++ __copy_tofrom_user_no_check(to, (__force void *)from, n) ++#define __copy_to_user_inatomic(to, from, n) \ ++ __copy_tofrom_user_no_check((__force void *)to, from, n) ++#define __copy_from_user_inatomic(to, from, n) \ ++ __copy_tofrom_user_no_check(to, (__force void *)from, n) ++ ++#define copy_to_user_ret(to, from, n, retval) \ ++ ({ if (copy_to_user(to, from, n)) return retval; }) ++ ++#define copy_from_user_ret(to, from, n, retval) \ ++ ({ if (copy_from_user(to, from, n)) return retval; }) ++ ++/* ++ * strncpy_from_user() ++ * Copy a null terminated string from userspace. ++ * ++ * dst - Destination in kernel space. The buffer must be at least count. ++ * src - Address of string in user space. ++ * count - Maximum number of bytes to copy (including the trailing NULL). ++ * ++ * Returns the length of the string (not including the trailing NULL. If ++ * count is smaller than the length of the string, we copy count bytes ++ * and return count. ++ * ++ */ ++static inline long strncpy_from_user(char *dst, const __user char *src, long count) ++{ ++ char *tmp; ++ if (!access_ok(VERIFY_READ, src, 1)) { ++ return -EFAULT; ++ } ++ ++ strncpy(dst, src, count); ++ for (tmp = dst; *tmp && count > 0; tmp++, count--) { ++ ; ++ } ++ return(tmp - dst); ++} ++ ++/* ++ * strnlen_user() ++ * Return the size of a string (including the ending 0) ++ * ++ * Return -EFAULT on exception, a value greater than <n> if too long ++ */ ++static inline long strnlen_user(const __user char *src, long n) ++{ ++ if (!access_ok(VERIFY_READ, src, 1)) { ++ return -EFAULT; ++ } ++ return(strlen(src) + 1); ++} ++ ++#define strlen_user(str) strnlen_user(str, 32767) ++ ++/* ++ * __clear_user() ++ * Zero Userspace ++ */ ++static inline unsigned long __clear_user(__user void *to, unsigned long n) ++{ ++ memset(to, 0, n); ++ return 0; ++} ++ ++/* ++ * clear_user() ++ * Zero user space (check for valid addresses) ++ */ ++static inline unsigned long clear_user(__user void *to, unsigned long n) ++{ ++ if (!access_ok(VERIFY_WRITE, to, n)) { ++ return -EFAULT; ++ } ++ return __clear_user(to, n); ++} ++ ++#endif /* _ASM_UBICOM32_UACCESS_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubi32-cs4384.h +@@ -0,0 +1,53 @@ ++/* ++ * arch/ubicom32/include/asm/ubi32-cs4384.h ++ * Ubicom32 architecture CS4384 driver platform data definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++#ifndef _ASM_UBICOM32_UBI32_CS4384_H ++#define _ASM_UBICOM32_UBI32_CS4384_H ++ ++enum ubi32_cs4384_mclk_source { ++ UBI32_CS4384_MCLK_PWM_0, ++ UBI32_CS4384_MCLK_PWM_1, ++ UBI32_CS4384_MCLK_PWM_2, ++ UBI32_CS4384_MCLK_CLKDIV_1, ++ UBI32_CS4384_MCLK_OTHER, ++}; ++ ++struct ubi32_cs4384_mclk_entry { ++ /* ++ * Rate, in Hz, of this entry ++ */ ++ int rate; ++ ++ /* ++ * The divider to program to get the rate ++ */ ++ int div; ++}; ++ ++struct ubi32_cs4384_platform_data { ++ enum ubi32_cs4384_mclk_source mclk_src; ++ ++ int n_mclk; ++ struct ubi32_cs4384_mclk_entry *mclk_entries; ++}; ++#endif /* _ASM_UBICOM32_UBI32_CS4384_H */ ++ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubi32-pcm.h +@@ -0,0 +1,53 @@ ++/* ++ * arch/ubicom32/include/asm/ubi32-pcm.h ++ * Ubicom32 architecture PCM driver platform data definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++#ifndef _ASM_UBICOM32_UBI32_PCM_H ++#define _ASM_UBICOM32_UBI32_PCM_H ++ ++/* ++ * This function is called when the sample rate has changed ++ */ ++typedef int (*ubi32_pcm_set_rate_fn_t)(void *appdata, int rate); ++ ++struct ubi32pcm_platform_data { ++ /* ++ * Name of the audiotio node ++ */ ++ const char *node_name; ++ ++ /* ++ * Application specific data provided when calling functions ++ */ ++ void *appdata; ++ ++ /* ++ * Functions called when various things happen ++ */ ++ ubi32_pcm_set_rate_fn_t set_rate; ++ ++ /* ++ * Pointer to optional upper layer data (i.e. DAC config, etc) ++ */ ++ void *priv_data; ++}; ++#endif /* _ASM_UBICOM32_UBI32_PCM_H */ ++ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32bl.h +@@ -0,0 +1,84 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32bl.h ++ * Ubicom32 architecture backlight driver platform data definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_UBICOM32_BL_H ++#define _ASM_UBICOM32_UBICOM32_BL_H ++ ++/* ++ * Different backlight control mechanisms ++ */ ++enum ubicom32bl_pwm_types { ++ /* ++ * PWM controlled backlight ++ */ ++ UBICOM32BL_TYPE_PWM, ++ ++ /* ++ * HRT based PWM backlight ++ */ ++ UBICOM32BL_TYPE_PWM_HRT, ++ ++ /* ++ * No dimming, just on or off ++ */ ++ UBICOM32BL_TYPE_BINARY, ++}; ++ ++struct ubicom32bl_platform_data { ++ /* ++ * Default intensity of the backlight 0-255 ++ */ ++ u8_t default_intensity; ++ ++ /* ++ * TRUE if the backlight sense is active low. (inverted) ++ * FALSE if the backlight sense is active high. ++ */ ++ bool invert; ++ ++ /* ++ * Type of the backlight ++ */ ++ enum ubicom32bl_pwm_types type; ++ ++ /* ++ * GPIO of the backlight if UBICOM32BL_TYPE_PWM_HRT, UBICOM32BL_TYPE_BINARY ++ */ ++ unsigned gpio; ++ ++ /* ++ * PWM channel and parameters of the backlight if UBICOM32BL_TYPE_PWM ++ * pre_scaler: sets the rate at which the PWM timer is clocked. (clk_core / 2^pre_scaler) ++ * period: sets the period of the timer in timer cycles ++ * The duty cycle will be directly proportional to the brightness setting. ++ */ ++ u32_t pwm_channel; ++ u8_t pwm_prescale; ++ u16_t pwm_period; ++}; ++ ++#endif /* _ASM_UBICOM32_UBICOM32_BL_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32-common-asm.h +@@ -0,0 +1,49 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32-common-asm.h ++ * Ubicom32 atomic lock operations. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_UBICOM32_COMMON_ASM_H ++#define _ASM_UBICOM32_UBICOM32_COMMON_ASM_H ++ ++/* ++ * atomic_lock_acquire macro ++ * Equivalent to __atomic_lock_acquire() ++ */ ++.macro atomic_lock_acquire ++ bset scratchpad1, scratchpad1, #ATOMIC_LOCK_BIT ++ jmpne.f .-4 ++.endm ++ ++/* ++ * atomic_lock_release macro ++ * Equivalent to __atomic_lock_release() ++ */ ++.macro atomic_lock_release ++ bclr scratchpad1, scratchpad1, #ATOMIC_LOCK_BIT ++.endm ++ ++#endif /* _ASM_UBICOM32_UBICOM32_COMMON_ASM_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32-common.h +@@ -0,0 +1,124 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32-common.h ++ * Ubicom32 atomic lock operations. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_UBICOM32_COMMON_H ++#define _ASM_UBICOM32_UBICOM32_COMMON_H ++ ++#define S(arg) #arg ++#define D(arg) S(arg) ++/* ++ * scratchpad1 is owned by the LDSR. ++ * ++ * The upper bits provide 16 global spinlocks. Acquiring one of these ++ * global spinlocks synchornizes across multiple threads and prevents ++ * the LDSR from delivering any interrupts while the lock is held. ++ * Use these locks only when absolutely required. ++ * ++ * The lower 16 bits of scratchpad1 are used as per thread interrupt ++ * enable/disable bits. These bits will prevent a thread from receiving ++ * any interrupts. ++ * ++ * Bit Usage: ++ * - MT_EN_LOCK_BIT - Protects writes to MT_EN, so code can read current value ++ * then write a new value atomically (profiler for example) ++ * - ATOMIC_LOCK_BIT - Used to provide general purpose atomic handling. ++ * - LDSR_LOCK_BIT - Used by the LDSR exclusively to provide protection. ++ * - LSB 16 bits - Used by the LDSR to represent thread enable/disable bits. ++ */ ++#define MT_EN_LOCK_BIT 31 ++#define ATOMIC_LOCK_BIT 30 ++#define LDSR_LOCK_BIT 29 ++#define PCI_LOCK_BIT 28 ++ ++#if !defined(__ASSEMBLY__) ++ ++#define UBICOM32_TRYLOCK(bit) \ ++ asm volatile ( \ ++ " move.4 %0, #0 \n\t" \ ++ " bset scratchpad1, scratchpad1, #"D(bit)" \n\t" \ ++ " jmpne.f 1f \n\t" \ ++ " move.4 %0, #1 \n\t" \ ++ "1: \n\t" \ ++ : "=r" (ret) \ ++ : \ ++ : "cc", "memory" \ ++ ); \ ++ ++#define UBICOM32_UNLOCK(bit) \ ++ asm volatile ( \ ++ " bclr scratchpad1, scratchpad1, #"D(bit)" \n\t" \ ++ : \ ++ : \ ++ : "cc", "memory" \ ++ ); \ ++ ++#define UBICOM32_LOCK(bit) \ ++ asm volatile ( \ ++ "1: bset scratchpad1, scratchpad1, #"D(bit)" \n\t" \ ++ " jmpne.f 1b \n\t" \ ++ : \ ++ : \ ++ : "cc", "memory" \ ++ ); \ ++ ++/* ++ * __atomic_lock_trylock() ++ * Attempt to acquire the lock, return TRUE if acquired. ++ */ ++static inline int __atomic_lock_trylock(void) ++{ ++ int ret; ++ UBICOM32_TRYLOCK(ATOMIC_LOCK_BIT); ++ return ret; ++} ++ ++/* ++ * __atomic_lock_release() ++ * Release the global atomic lock. ++ * ++ * Note: no one is suspended waiting since this lock is a spinning lock. ++ */ ++static inline void __atomic_lock_release(void) ++{ ++ UBICOM32_UNLOCK(ATOMIC_LOCK_BIT); ++} ++ ++/* ++ * __atomic_lock_acquire() ++ * Acquire the global atomic lock, spin if not available. ++ */ ++static inline void __atomic_lock_acquire(void) ++{ ++ UBICOM32_LOCK(ATOMIC_LOCK_BIT); ++} ++#else /* __ASSEMBLY__ */ ++ ++#include <asm/ubicom32-common-asm.h> ++ ++#endif /* __ASSEMBLY__ */ ++#endif /* _ASM_UBICOM32_UBICOM32_COMMON_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32fb.h +@@ -0,0 +1,56 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32fb.h ++ * Ubicom32 architecture video frame buffer definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++#ifndef _ASM_UBICOM32_UBICOM32FB_H ++#define _ASM_UBICOM32_UBICOM32FB_H ++ ++#include <linux/ioctl.h> ++ ++/* ++ * Set next frame ++ */ ++#define UBICOM32FB_IOCTL_SET_NEXT_FRAME _IOW('r', 1, void *) ++#define UBICOM32FB_IOCTL_SET_NEXT_FRAME_SYNC _IOW('r', 2, void *) ++ ++/* ++ * Set Mode ++ */ ++#define UBICOM32FB_IOCTL_SET_MODE _IOW('r', 3, void *) ++struct ubicom32fb_mode { ++ unsigned long width; ++ unsigned long height; ++ unsigned long flags; ++ void *next_frame; ++}; ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_YUV_SCAN_ORDER (1 << 8) ++ ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_YUV_BLOCK_ORDER (1 << 7) ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_YUV (1 << 6) ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_VSUB (1 << 5) ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_VRANGE_16_255 (1 << 4) ++ ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_VRANGE_0_255 (1 << 3) ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_HSUB_2_1 (1 << 2) ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_HSUB_1_1 (1 << 1) ++#define UBICOM32FB_IOCTL_SET_MODE_FLAG_SCALE_ENABLE (1 << 0) ++ ++#endif /* _ASM_UBICOM32_UBICOM32FB_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32hid.h +@@ -0,0 +1,133 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32hid.h ++ * Ubicom32 architecture HID driver platform data definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_UBICOM32_HID_H ++#define _ASM_UBICOM32_UBICOM32_HID_H ++ ++enum ubicom32hid_bl_types { ++ /* ++ * On or off, using command SET_BL_EN, PB4 ++ */ ++ UBICOM32HID_BL_TYPE_BINARY, ++ ++ /* ++ * Dimmable, using command SET_PWM, PB3 ++ */ ++ UBICOM32HID_BL_TYPE_PWM, ++}; ++ ++/* ++ * IR code mapping to event code. ++ * If there are no button mappings and no ir mappings ++ * then no input driver will be registered. ++ */ ++struct ubicom32hid_ir { ++ /* ++ * Input event code (KEY_*, SW_*, etc) ++ */ ++ int code; ++ ++ /* ++ * Input event type (EV_KEY, EV_SW, etc) ++ */ ++ int type; ++ ++ /* ++ * The IR code of this button. ++ */ ++ uint32_t ir_code; ++}; ++ ++/* ++ * Button mapping to event code. ++ * If there are no button mappings and no ir mappings ++ * then no input driver will be registered. ++ */ ++struct ubicom32hid_button { ++ /* ++ * Input event code (KEY_*, SW_*, etc) ++ */ ++ int code; ++ ++ /* ++ * Input event type (EV_KEY, EV_SW, etc) ++ */ ++ int type; ++ ++ /* ++ * Bit number of this button. ++ */ ++ uint8_t bit; ++}; ++ ++struct ubicom32hid_platform_data { ++ /* ++ * Default intensity of the backlight 0-255 ++ */ ++ u8_t default_intensity; ++ ++ /* ++ * GPIO number of the reset line and its polarity. ++ */ ++ unsigned gpio_reset; ++ int gpio_reset_polarity; ++ ++ /* ++ * TRUE if the backlight sense is active low. (inverted) ++ * FALSE if the backlight sense is active high. ++ */ ++ bool invert; ++ ++ /* ++ * Type of the backlight we are controlling ++ */ ++ enum ubicom32hid_bl_types type; ++ ++ /* ++ * Optional polling rate for input, in ms, defaults to 100ms ++ */ ++ int poll_interval; ++ ++ /* ++ * Optional name to register as input device ++ */ ++ const char *input_name; ++ ++ /* ++ * Button mapping array ++ */ ++ const struct ubicom32hid_button *buttons; ++ int nbuttons; ++ ++ /* ++ * IR mapping array ++ */ ++ const struct ubicom32hid_ir *ircodes; ++ int nircodes; ++}; ++ ++#endif /* _ASM_UBICOM32_UBICOM32_HID_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32input.h +@@ -0,0 +1,76 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32input.h ++ * Ubicom32 Input driver, based on gpio-keys ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * ++ * TODO: add groups for inputs which can be sampled together ++ */ ++ ++#ifndef _ASM_UBICOM32_UBICOM32_INPUT_H ++#define _ASM_UBICOM32_UBICOM32_INPUT_H ++ ++struct ubicom32input_button { ++ /* ++ * Input event code (KEY_*, SW_*, etc) ++ */ ++ int code; ++ ++ /* ++ * Input event type (EV_KEY, EV_SW, etc) ++ */ ++ int type; ++ ++ /* ++ * GPIO to poll ++ */ ++ int gpio; ++ ++ /* ++ * 1 for active low, 0 for active high ++ */ ++ int active_low; ++ ++ /* ++ * Description, used for reserving GPIOs ++ */ ++ const char *desc; ++}; ++ ++struct ubicom32input_platform_data { ++ struct ubicom32input_button *buttons; ++ int nbuttons; ++ ++ /* ++ * Optional poll interval, in ms, defaults to 50ms ++ */ ++ int poll_interval; ++ ++ /* ++ * Option Name of this driver ++ */ ++ const char *name; ++}; ++ ++#endif /* _ASM_UBICOM32_UBICOM32_INPUT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32input_i2c.h +@@ -0,0 +1,71 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32input_i2c.h ++ * Ubicom32 architecture Input driver over I2C platform data definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * ++ * TODO: add groups for inputs which can be sampled together ++ */ ++ ++#ifndef _ASM_UBICOM32_UBICOM32_INPUT_I2C_H ++#define _ASM_UBICOM32_UBICOM32_INPUT_I2C_H ++ ++struct ubicom32input_i2c_button { ++ /* ++ * Input event code (KEY_*, SW_*, etc) ++ */ ++ int code; ++ ++ /* ++ * Input event type (EV_KEY, EV_SW, etc) ++ */ ++ int type; ++ ++ /* ++ * Bit number of this button. (0 - ngpio) ++ */ ++ int bit; ++ ++ /* ++ * 1 for active low, 0 for active high ++ */ ++ int active_low; ++}; ++ ++struct ubicom32input_i2c_platform_data { ++ struct ubicom32input_i2c_button *buttons; ++ int nbuttons; ++ ++ /* ++ * Optional poll interval, in ms, defaults to 100ms ++ */ ++ int poll_interval; ++ ++ /* ++ * Option Name of this driver ++ */ ++ const char *name; ++}; ++ ++#endif /* _ASM_UBICOM32_UBICOM32_INPUT_I2C_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32lcd.h +@@ -0,0 +1,39 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32lcd.h ++ * Ubicom32 architecture LCD driver platform data definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_UBICOM32_LCD_H ++#define _ASM_UBICOM32_UBICOM32_LCD_H ++ ++struct ubicom32lcd_platform_data { ++ /* ++ * GPIO and polarity for VGH signal. A FALSE polarity is active low, TRUE is active high. ++ */ ++ int vgh_gpio; ++ bool vgh_polarity; ++}; ++ ++#endif /* _ASM_UBICOM32_UBICOM32_LCD_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32ring.h +@@ -0,0 +1,103 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32ring.h ++ * Userspace I/O platform driver for Ubicom32 ring buffers ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef _ASM_UBICOM32_UBICOM32RING_H ++#define _ASM_UBICOM32_UBICOM32RING_H ++ ++#define UIO_UBICOM32RING_REG_VERSION 2 ++ ++struct uio_ubicom32ring_desc { ++ volatile unsigned int head; ++ volatile unsigned int tail; ++ unsigned int entries; ++ volatile unsigned int ring[0]; ++}; ++ ++struct uio_ubicom32ring_regs { ++ unsigned int version; ++ ++ /* ++ * Magic type used to identify the ring set. Each driver will ++ * have a different magic value. ++ */ ++ unsigned int magic; ++ ++ /* ++ * Registers defined by the driver ++ */ ++ unsigned int regs_size; ++ void *regs; ++ ++ /* ++ * The locations of the rings ++ * ++ * DO NOT ADD ANYTHING BELOW THIS LINE ++ */ ++ unsigned int num_rings; ++ struct uio_ubicom32ring_desc *rings[0]; ++}; ++ ++/* ++ * ringtio_ring_flush ++ */ ++static inline void ringtio_ring_flush(struct uio_ubicom32ring_desc *rd) ++{ ++ rd->head = rd->tail = 0; ++} ++ ++/* ++ * ringtio_ring_get ++ */ ++static inline int ringtio_ring_get(struct uio_ubicom32ring_desc *rd, void **val) ++{ ++ if (rd->head == rd->tail) { ++ return 0; ++ } ++ ++ *val = (void *)rd->ring[rd->head++]; ++ if (rd->head == rd->entries) { ++ rd->head = 0; ++ } ++ return 1; ++} ++ ++/* ++ * ringtio_ring_put ++ */ ++static inline int ringtio_ring_put(struct uio_ubicom32ring_desc *rd, void *val) ++{ ++ unsigned int newtail = rd->tail + 1; ++ if (newtail == rd->entries) { ++ newtail = 0; ++ } ++ ++ if (newtail == rd->head) { ++ return 0; ++ } ++ ++ rd->ring[rd->tail] = (unsigned int)val; ++ rd->tail = newtail; ++ return 1; ++} ++ ++#endif /* _ASM_UBICOM32_UBICOM32RING_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32sd.h +@@ -0,0 +1,45 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32sd.h ++ * Ubicom32SD public include file ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++#ifndef _ASM_UBICOM32_UBICOM32_SD_H ++#define _ASM_UBICOM32_UBICOM32_SD_H ++ ++struct ubicom32sd_card { ++ /* ++ * GPIOs of PWR, WP and CD lines. ++ * Polarity is 1 for active high and 0 for active low ++ */ ++ int pin_pwr; ++ bool pwr_polarity; ++ int pin_wp; ++ bool wp_polarity; ++ int pin_cd; ++ bool cd_polarity; ++}; ++ ++struct ubicom32sd_platform_data { ++ int ncards; ++ ++ struct ubicom32sd_card *cards; ++}; ++ ++#endif /* _ASM_UBICOM32_UBICOM32_SD_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32-spi-gpio.h +@@ -0,0 +1,62 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32-spi-gpio.h ++ * Platform driver data definitions for GPIO based SPI driver. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_UBICOM32_SPI_GPIO_H ++#define _ASM_UBICOM32_UBICOM32_SPI_GPIO_H ++ ++struct ubicom32_spi_gpio_platform_data { ++ /* ++ * GPIO to use for MOSI, MISO, CLK ++ */ ++ int pin_mosi; ++ int pin_miso; ++ int pin_clk; ++ ++ /* ++ * Default state of CLK line ++ */ ++ int clk_default; ++ ++ /* ++ * Number of chip selects on this bus ++ */ ++ int num_chipselect; ++ ++ /* ++ * The bus number of this chip ++ */ ++ int bus_num; ++}; ++ ++struct ubicom32_spi_gpio_controller_data { ++ /* ++ * GPIO to use for chip select ++ */ ++ int pin_cs; ++}; ++ ++#endif /* _ASM_UBICOM32_UBICOM32_SPI_GPIO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32suart.h +@@ -0,0 +1,36 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32suart.h ++ * <TODO: Replace with short file description> ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_UBICOM32_SUART_H ++#define _ASM_UBICOM32_UBICOM32_SUART_H ++ ++/* ++ * Platform resource id for serdes uart clock parameter ++ */ ++#define UBICOM32_SUART_IORESOURCE_CLOCK (1) ++ ++#endif /* _ASM_UBICOM32_UBICOM32_SUART_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ubicom32-tio.h +@@ -0,0 +1,42 @@ ++/* ++ * arch/ubicom32/include/asm/ubicom32-tio.h ++ * Threaded I/O interface definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_UBICOM32_TIO_H ++#define _ASM_UBICOM32_UBICOM32_TIO_H ++ ++extern u8_t usb_tio_read_u16(u32_t address, u16_t *data); ++extern u8_t usb_tio_read_u8(u32_t address, u8_t *data); ++ ++extern u8_t usb_tio_write_u16(u32_t address, u16_t data); ++extern u8_t usb_tio_write_u8(u32_t address, u8_t data); ++ ++extern u8_t usb_tio_read_fifo(u32_t address, u32_t buffer, u32_t bytes); ++extern u8_t usb_tio_write_fifo(u32_t address, u32_t buffer, u32_t bytes); ++extern u8_t usb_tio_write_fifo_sync(u32_t address, u32_t buffer, u32_t bytes); ++extern void usb_tio_read_int_status(u8_t *int_usb, u16_t *int_tx, u16_t *int_rx); ++ ++#endif /* _ASM_UBICOM32_UBICOM32_TIO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/ucontext.h +@@ -0,0 +1,39 @@ ++/* ++ * arch/ubicom32/include/asm/ucontext.h ++ * Definition of ucontext structure for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_UCONTEXT_H ++#define _ASM_UBICOM32_UCONTEXT_H ++ ++struct ucontext { ++ unsigned long uc_flags; ++ struct ucontext *uc_link; ++ stack_t uc_stack; ++ struct sigcontext uc_mcontext; ++ sigset_t uc_sigmask; /* mask last for extensibility */ ++}; ++ ++#endif /* _ASM_UBICOM32_UCONTEXT_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/unaligned.h +@@ -0,0 +1,44 @@ ++/* ++ * arch/ubicom32/include/asm/unaligned.h ++ * Ubicom32 architecture unaligned memory access definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * ++ * TODO: This is a copy of arm unaligned handling that probably needs ++ * to be optimized for UBICOM32, but it works for now. ++ */ ++ ++#ifndef _ASM_UBICOM32_UNALIGNED_H ++#define _ASM_UBICOM32_UNALIGNED_H ++ ++#include <asm/types.h> ++ ++#include <linux/unaligned/le_byteshift.h> ++#include <linux/unaligned/be_byteshift.h> ++#include <linux/unaligned/generic.h> ++ ++#define get_unaligned __get_unaligned_be ++#define put_unaligned __put_unaligned_be ++ ++#endif /* _ASM_UBICOM32_UNALIGNED_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/unistd.h +@@ -0,0 +1,400 @@ ++/* ++ * arch/ubicom32/include/asm/unistd.h ++ * Ubicom32 architecture syscall definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_UNISTD_H ++#define _ASM_UBICOM32_UNISTD_H ++ ++/* ++ * This file contains the system call numbers. ++ */ ++ ++#define __NR_restart_syscall 0 ++#define __NR_exit 1 ++#define __NR_fork 2 ++#define __NR_read 3 ++#define __NR_write 4 ++#define __NR_open 5 ++#define __NR_close 6 ++#define __NR_waitpid 7 ++#define __NR_creat 8 ++#define __NR_link 9 ++#define __NR_unlink 10 ++#define __NR_execve 11 ++#define __NR_chdir 12 ++#define __NR_time 13 ++#define __NR_mknod 14 ++#define __NR_chmod 15 ++#define __NR_chown 16 ++#define __NR_break 17 ++#define __NR_oldstat 18 ++#define __NR_lseek 19 ++#define __NR_getpid 20 ++#define __NR_mount 21 ++#define __NR_umount 22 ++#define __NR_setuid 23 ++#define __NR_getuid 24 ++#define __NR_stime 25 ++#define __NR_ptrace 26 ++#define __NR_alarm 27 ++#define __NR_oldfstat 28 ++#define __NR_pause 29 ++#define __NR_utime 30 ++#define __NR_stty 31 ++#define __NR_gtty 32 ++#define __NR_access 33 ++#define __NR_nice 34 ++#define __NR_ftime 35 ++#define __NR_sync 36 ++#define __NR_kill 37 ++#define __NR_rename 38 ++#define __NR_mkdir 39 ++#define __NR_rmdir 40 ++#define __NR_dup 41 ++#define __NR_pipe 42 ++#define __NR_times 43 ++#define __NR_prof 44 ++#define __NR_brk 45 ++#define __NR_setgid 46 ++#define __NR_getgid 47 ++#define __NR_signal 48 ++#define __NR_geteuid 49 ++#define __NR_getegid 50 ++#define __NR_acct 51 ++#define __NR_umount2 52 ++#define __NR_lock 53 ++#define __NR_ioctl 54 ++#define __NR_fcntl 55 ++#define __NR_mpx 56 ++#define __NR_setpgid 57 ++#define __NR_ulimit 58 ++#define __NR_oldolduname 59 ++#define __NR_umask 60 ++#define __NR_chroot 61 ++#define __NR_ustat 62 ++#define __NR_dup2 63 ++#define __NR_getppid 64 ++#define __NR_getpgrp 65 ++#define __NR_setsid 66 ++#define __NR_sigaction 67 ++#define __NR_sgetmask 68 ++#define __NR_ssetmask 69 ++#define __NR_setreuid 70 ++#define __NR_setregid 71 ++#define __NR_sigsuspend 72 ++#define __NR_sigpending 73 ++#define __NR_sethostname 74 ++#define __NR_setrlimit 75 ++#define __NR_getrlimit 76 ++#define __NR_getrusage 77 ++#define __NR_gettimeofday 78 ++#define __NR_settimeofday 79 ++#define __NR_getgroups 80 ++#define __NR_setgroups 81 ++#define __NR_select 82 ++#define __NR_symlink 83 ++#define __NR_oldlstat 84 ++#define __NR_readlink 85 ++#define __NR_uselib 86 ++#define __NR_swapon 87 ++#define __NR_reboot 88 ++#define __NR_readdir 89 ++#define __NR_mmap 90 ++#define __NR_munmap 91 ++#define __NR_truncate 92 ++#define __NR_ftruncate 93 ++#define __NR_fchmod 94 ++#define __NR_fchown 95 ++#define __NR_getpriority 96 ++#define __NR_setpriority 97 ++#define __NR_profil 98 ++#define __NR_statfs 99 ++#define __NR_fstatfs 100 ++#define __NR_ioperm 101 ++#define __NR_socketcall 102 ++#define __NR_syslog 103 ++#define __NR_setitimer 104 ++#define __NR_getitimer 105 ++#define __NR_stat 106 ++#define __NR_lstat 107 ++#define __NR_fstat 108 ++#define __NR_olduname 109 ++#define __NR_iopl /* 110 */ not supported ++#define __NR_vhangup 111 ++#define __NR_idle /* 112 */ Obsolete ++#define __NR_vm86 /* 113 */ not supported ++#define __NR_wait4 114 ++#define __NR_swapoff 115 ++#define __NR_sysinfo 116 ++#define __NR_ipc 117 ++#define __NR_fsync 118 ++#define __NR_sigreturn 119 ++#define __NR_clone 120 ++#define __NR_setdomainname 121 ++#define __NR_uname 122 ++#define __NR_cacheflush 123 ++#define __NR_adjtimex 124 ++#define __NR_mprotect 125 ++#define __NR_sigprocmask 126 ++#define __NR_create_module 127 ++#define __NR_init_module 128 ++#define __NR_delete_module 129 ++#define __NR_get_kernel_syms 130 ++#define __NR_quotactl 131 ++#define __NR_getpgid 132 ++#define __NR_fchdir 133 ++#define __NR_bdflush 134 ++#define __NR_sysfs 135 ++#define __NR_personality 136 ++#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ ++#define __NR_setfsuid 138 ++#define __NR_setfsgid 139 ++#define __NR__llseek 140 ++#define __NR_getdents 141 ++#define __NR__newselect 142 ++#define __NR_flock 143 ++#define __NR_msync 144 ++#define __NR_readv 145 ++#define __NR_writev 146 ++#define __NR_getsid 147 ++#define __NR_fdatasync 148 ++#define __NR__sysctl 149 ++#define __NR_mlock 150 ++#define __NR_munlock 151 ++#define __NR_mlockall 152 ++#define __NR_munlockall 153 ++#define __NR_sched_setparam 154 ++#define __NR_sched_getparam 155 ++#define __NR_sched_setscheduler 156 ++#define __NR_sched_getscheduler 157 ++#define __NR_sched_yield 158 ++#define __NR_sched_get_priority_max 159 ++#define __NR_sched_get_priority_min 160 ++#define __NR_sched_rr_get_interval 161 ++#define __NR_nanosleep 162 ++#define __NR_mremap 163 ++#define __NR_setresuid 164 ++#define __NR_getresuid 165 ++#define __NR_getpagesize 166 ++#define __NR_query_module 167 ++#define __NR_poll 168 ++#define __NR_nfsservctl 169 ++#define __NR_setresgid 170 ++#define __NR_getresgid 171 ++#define __NR_prctl 172 ++#define __NR_rt_sigreturn 173 ++#define __NR_rt_sigaction 174 ++#define __NR_rt_sigprocmask 175 ++#define __NR_rt_sigpending 176 ++#define __NR_rt_sigtimedwait 177 ++#define __NR_rt_sigqueueinfo 178 ++#define __NR_rt_sigsuspend 179 ++#define __NR_pread64 180 ++#define __NR_pwrite64 181 ++#define __NR_lchown 182 ++#define __NR_getcwd 183 ++#define __NR_capget 184 ++#define __NR_capset 185 ++#define __NR_sigaltstack 186 ++#define __NR_sendfile 187 ++#define __NR_getpmsg 188 /* some people actually want streams */ ++#define __NR_putpmsg 189 /* some people actually want streams */ ++#define __NR_vfork 190 ++#define __NR_ugetrlimit 191 ++#define __NR_mmap2 192 ++#define __NR_truncate64 193 ++#define __NR_ftruncate64 194 ++#define __NR_stat64 195 ++#define __NR_lstat64 196 ++#define __NR_fstat64 197 ++#define __NR_chown32 198 ++#define __NR_getuid32 199 ++#define __NR_getgid32 200 ++#define __NR_geteuid32 201 ++#define __NR_getegid32 202 ++#define __NR_setreuid32 203 ++#define __NR_setregid32 204 ++#define __NR_getgroups32 205 ++#define __NR_setgroups32 206 ++#define __NR_fchown32 207 ++#define __NR_setresuid32 208 ++#define __NR_getresuid32 209 ++#define __NR_setresgid32 210 ++#define __NR_getresgid32 211 ++#define __NR_lchown32 212 ++#define __NR_setuid32 213 ++#define __NR_setgid32 214 ++#define __NR_setfsuid32 215 ++#define __NR_setfsgid32 216 ++#define __NR_pivot_root 217 ++#define __NR_getdents64 220 ++#define __NR_gettid 221 ++#define __NR_tkill 222 ++#define __NR_setxattr 223 ++#define __NR_lsetxattr 224 ++#define __NR_fsetxattr 225 ++#define __NR_getxattr 226 ++#define __NR_lgetxattr 227 ++#define __NR_fgetxattr 228 ++#define __NR_listxattr 229 ++#define __NR_llistxattr 230 ++#define __NR_flistxattr 231 ++#define __NR_removexattr 232 ++#define __NR_lremovexattr 233 ++#define __NR_fremovexattr 234 ++#define __NR_futex 235 ++#define __NR_sendfile64 236 ++#define __NR_mincore 237 ++#define __NR_madvise 238 ++#define __NR_fcntl64 239 ++#define __NR_readahead 240 ++#define __NR_io_setup 241 ++#define __NR_io_destroy 242 ++#define __NR_io_getevents 243 ++#define __NR_io_submit 244 ++#define __NR_io_cancel 245 ++#define __NR_fadvise64 246 ++#define __NR_exit_group 247 ++#define __NR_lookup_dcookie 248 ++#define __NR_epoll_create 249 ++#define __NR_epoll_ctl 250 ++#define __NR_epoll_wait 251 ++#define __NR_remap_file_pages 252 ++#define __NR_set_tid_address 253 ++#define __NR_timer_create 254 ++#define __NR_timer_settime 255 ++#define __NR_timer_gettime 256 ++#define __NR_timer_getoverrun 257 ++#define __NR_timer_delete 258 ++#define __NR_clock_settime 259 ++#define __NR_clock_gettime 260 ++#define __NR_clock_getres 261 ++#define __NR_clock_nanosleep 262 ++#define __NR_statfs64 263 ++#define __NR_fstatfs64 264 ++#define __NR_tgkill 265 ++#define __NR_utimes 266 ++#define __NR_fadvise64_64 267 ++#define __NR_mbind 268 ++#define __NR_get_mempolicy 269 ++#define __NR_set_mempolicy 270 ++#define __NR_mq_open 271 ++#define __NR_mq_unlink 272 ++#define __NR_mq_timedsend 273 ++#define __NR_mq_timedreceive 274 ++#define __NR_mq_notify 275 ++#define __NR_mq_getsetattr 276 ++#define __NR_waitid 277 ++#define __NR_vserver 278 ++#define __NR_add_key 279 ++#define __NR_request_key 280 ++#define __NR_keyctl 281 ++#define __NR_ioprio_set 282 ++#define __NR_ioprio_get 283 ++#define __NR_inotify_init 284 ++#define __NR_inotify_add_watch 285 ++#define __NR_inotify_rm_watch 286 ++#define __NR_migrate_pages 287 ++#define __NR_openat 288 ++#define __NR_mkdirat 289 ++#define __NR_mknodat 290 ++#define __NR_fchownat 291 ++#define __NR_futimesat 292 ++#define __NR_fstatat64 293 ++#define __NR_unlinkat 294 ++#define __NR_renameat 295 ++#define __NR_linkat 296 ++#define __NR_symlinkat 297 ++#define __NR_readlinkat 298 ++#define __NR_fchmodat 299 ++#define __NR_faccessat 300 ++#define __NR_pselect6 301 ++#define __NR_ppoll 302 ++#define __NR_unshare 303 ++#define __NR_set_robust_list 304 ++#define __NR_get_robust_list 305 ++#define __NR_splice 306 ++#define __NR_sync_file_range 307 ++#define __NR_tee 308 ++#define __NR_vmsplice 309 ++#define __NR_move_pages 310 ++#define __NR_sched_setaffinity 311 ++#define __NR_sched_getaffinity 312 ++#define __NR_kexec_load 313 ++#define __NR_getcpu 314 ++#define __NR_epoll_pwait 315 ++#define __NR_utimensat 316 ++#define __NR_signalfd 317 ++#define __NR_timerfd_create 318 ++#define __NR_eventfd 319 ++#define __NR_fallocate 320 ++#define __NR_timerfd_settime 321 ++#define __NR_timerfd_gettime 322 ++#define __NR_signalfd4 323 ++#define __NR_eventfd2 324 ++#define __NR_epoll_create1 325 ++#define __NR_dup3 326 ++#define __NR_pipe2 327 ++#define __NR_inotify_init1 328 ++ ++#ifdef __KERNEL__ ++ ++#define NR_syscalls 329 ++ ++#define __ARCH_WANT_IPC_PARSE_VERSION ++#define __ARCH_WANT_OLD_READDIR ++#define __ARCH_WANT_OLD_STAT ++#define __ARCH_WANT_STAT64 ++#define __ARCH_WANT_SYS_ALARM ++#define __ARCH_WANT_SYS_GETHOSTNAME ++#define __ARCH_WANT_SYS_PAUSE ++#define __ARCH_WANT_SYS_SGETMASK ++#define __ARCH_WANT_SYS_SIGNAL ++#define __ARCH_WANT_SYS_TIME ++#define __ARCH_WANT_SYS_UTIME ++#define __ARCH_WANT_SYS_WAITPID ++#define __ARCH_WANT_SYS_SOCKETCALL ++#define __ARCH_WANT_SYS_FADVISE64 ++#define __ARCH_WANT_SYS_GETPGRP ++#define __ARCH_WANT_SYS_LLSEEK ++#define __ARCH_WANT_SYS_NICE ++#define __ARCH_WANT_SYS_OLD_GETRLIMIT ++#define __ARCH_WANT_SYS_OLDUMOUNT ++#define __ARCH_WANT_SYS_SIGPENDING ++#define __ARCH_WANT_SYS_SIGPROCMASK ++#define __ARCH_WANT_SYS_RT_SIGACTION ++ ++/* ++ * "Conditional" syscalls ++ * ++ * What we want is __attribute__((weak,alias("sys_ni_syscall"))), ++ * but it doesn't work on all toolchains, so we just do it by hand ++ */ ++//#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") ++#define cond_syscall(x) long x(void) __attribute__((weak,alias("sys_ni_syscall"))) ++#endif /* __KERNEL__ */ ++ ++#endif /* _ASM_UBICOM32_UNISTD_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/user.h +@@ -0,0 +1,82 @@ ++/* ++ * arch/ubicom32/include/asm/user.h ++ * Ubicom32 architecture core file definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_USER_H ++#define _ASM_UBICOM32_USER_H ++ ++#include <asm/ptrace.h> ++#include <asm/page.h> ++/* ++ * Adapted from <asm-powerpc/user.h> ++ * ++ * Core file format: The core file is written in such a way that gdb ++ * can understand it and provide useful information to the user (under ++ * linux we use the `trad-core' bfd, NOT the osf-core). The file contents ++ * are as follows: ++ * ++ * upage: 1 page consisting of a user struct that tells gdb ++ * what is present in the file. Directly after this is a ++ * copy of the task_struct, which is currently not used by gdb, ++ * but it may come in handy at some point. All of the registers ++ * are stored as part of the upage. The upage should always be ++ * only one page long. ++ * data: The data segment follows next. We use current->end_text to ++ * current->brk to pick up all of the user variables, plus any memory ++ * that may have been sbrk'ed. No attempt is made to determine if a ++ * page is demand-zero or if a page is totally unused, we just cover ++ * the entire range. All of the addresses are rounded in such a way ++ * that an integral number of pages is written. ++ * stack: We need the stack information in order to get a meaningful ++ * backtrace. We need to write the data from usp to ++ * current->start_stack, so we round each of these in order to be able ++ * to write an integer number of pages. ++ */ ++ ++struct user_ubicom32fp_struct { ++}; ++ ++struct user { ++ struct pt_regs regs; /* entire machine state */ ++ size_t u_tsize; /* text size (pages) */ ++ size_t u_dsize; /* data size (pages) */ ++ size_t u_ssize; /* stack size (pages) */ ++ unsigned long start_code; /* text starting address */ ++ unsigned long start_data; /* data starting address */ ++ unsigned long start_stack; /* stack starting address */ ++ long int signal; /* signal causing core dump */ ++ unsigned long u_ar0; /* help gdb find registers */ ++ unsigned long magic; /* identifies a core file */ ++ char u_comm[32]; /* user command name */ ++}; ++ ++#define NBPG PAGE_SIZE ++#define UPAGES 1 ++#define HOST_TEXT_START_ADDR (u.start_code) ++#define HOST_DATA_START_ADDR (u.start_data) ++#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) ++ ++#endif /* _ASM_UBICOM32_USER_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/vdc_tio.h +@@ -0,0 +1,129 @@ ++/* ++ * arch/ubicom32/include/asm/vdc_tio.h ++ * Ubicom32 architecture VDC TIO definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_VDC_TIO_H ++#define _ASM_UBICOM32_VDC_TIO_H ++ ++#include <asm/devtree.h> ++ ++#define VDCTIO_VP_VERSION 5 ++ ++#define VDCTIO_SCALE_FLAG_VSUB (1 << 9) ++#define VDCTIO_SCALE_FLAG_YUV_SCAN_ORDER (1 << 8) ++#define VDCTIO_SCALE_FLAG_YUV_BLOCK_ORDER (1 << 7) ++#define VDCTIO_SCALE_FLAG_YUV (1 << 6) ++#define VDCTIO_SCALE_FLAG_VRANGE_16_255 (1 << 5) ++#define VDCTIO_SCALE_FLAG_VRANGE_0_255 (1 << 4) ++#define VDCTIO_SCALE_FLAG_HSUB_2_1 (1 << 3) ++#define VDCTIO_SCALE_FLAG_HSUB_1_1 (1 << 2) ++#define VDCTIO_SCALE_FLAG_SET_FRAME_BUFFER (1 << 1) ++#define VDCTIO_SCALE_FLAG_ENABLE (1 << 0) ++ ++#define VDCTIO_NEXT_FRAME_FLAG_YUV_BIT 0 ++#define VDCTIO_NEXT_FRAME_FLAG_YUV (1 << (VDCTIO_NEXT_FRAME_FLAG_YUV_BIT)) ++ ++#define VDCTIO_CAPS_SUPPORTS_SCALING (1 << 0) ++ ++#define VDCTIO_COMMAND_START (1 << 3) ++#define VDCTIO_COMMAND_SET_COEFF (1 << 2) ++#define VDCTIO_COMMAND_SET_LUT (1 << 1) ++#define VDCTIO_COMMAND_SET_SCALE_MODE (1 << 0) ++ ++/* ++ * Command / Data registers to access the VDC ++ */ ++struct vdc_tio_vp_regs { ++ /* ++ * Version of this TIO register map ++ */ ++ u32_t version; ++ ++ volatile u32_t command; ++ ++ /* ++ * Next frame pointer, when the command VDCTIO_COMMAND_SET_FRAME_BUFFER is set, ++ * the vdc will take the pointer here and display it. ++ */ ++ void *next_frame; ++ u32_t next_frame_flags; ++ ++ /* ++ * These map directly into the PIXP registers 0x20-0x80. ++ * DO NOT change the order of these three variables. ++ */ ++ u32_t red_lut[6]; ++ u32_t blue_lut[6]; ++ u32_t green_lut[13]; ++ ++ /* ++ * These map directly into the PIXP registers 0x04, 0x08 ++ */ ++ u32_t coeff0; ++ u32_t coeff1; ++ ++ /* ++ * There are used to set the scaling parameters ++ */ ++ u32_t x_in; ++ u32_t x_out; ++ u32_t y_in; ++ u32_t y_out; ++ u32_t scale_flags; ++ ++ /* ++ * Current frame number, monotonically increasing number ++ */ ++ u32_t frame_number; ++ ++ /* ++ * These variables tell the guest OS what the underlying hardware looks like ++ */ ++ u32_t caps; ++ u32_t xres; ++ u32_t yres; ++ u32_t fb_align; ++ u8_t bpp; ++ u8_t rbits; ++ u8_t gbits; ++ u8_t bbits; ++ u8_t rshift; ++ u8_t gshift; ++ u8_t bshift; ++}; ++ ++/* ++ * Devtree node for VDC ++ */ ++struct vdc_tio_node { ++ struct devtree_node dn; ++ ++ struct vdc_tio_vp_regs *regs; ++}; ++ ++extern void vdc_tio_init(void); ++ ++#endif /* _ASM_UBICOM32_VDC_TIO_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/vga.h +@@ -0,0 +1,71 @@ ++/* ++ * arch/ubicom32/include/asm/vga.h ++ * Ubicom32 low level VGA/frame buffer definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * (c) 1998 Martin Mares <mj@ucw.cz> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#ifndef _ASM_UBICOM32_VGA_H ++#define _ASM_UBICOM32_VGA_H ++ ++#include <asm/byteorder.h> ++ ++/* ++ * On the PC, we can just recalculate addresses and then ++ * access the videoram directly without any black magic. ++ */ ++ ++#define VGA_MAP_MEM(x, s) (0xb0000000L + (unsigned long)(x)) ++ ++#define vga_readb(x) (*(x)) ++#define vga_writeb(x, y) (*(y) = (x)) ++ ++#define VT_BUF_HAVE_RW ++/* ++ * These are only needed for supporting VGA or MDA text mode, which use little ++ * endian byte ordering. ++ * In other cases, we can optimize by using native byte ordering and ++ * <linux/vt_buffer.h> has already done the right job for us. ++ */ ++ ++#undef scr_writew ++#undef scr_readw ++ ++static inline void scr_writew(u16 val, volatile u16 *addr) ++{ ++ *addr = cpu_to_le16(val); ++} ++ ++static inline u16 scr_readw(volatile const u16 *addr) ++{ ++ return le16_to_cpu(*addr); ++} ++ ++#define scr_memcpyw(d, s, c) memcpy(d, s, c) ++#define scr_memmovew(d, s, c) memmove(d, s, c) ++#define VT_BUF_HAVE_MEMCPYW ++#define VT_BUF_HAVE_MEMMOVEW ++ ++#endif /* _ASM_UBICOM32_VGA_H */ +--- /dev/null ++++ b/arch/ubicom32/include/asm/xor.h +@@ -0,0 +1,33 @@ ++/* ++ * arch/ubicom32/include/asm/xor.h ++ * Generic xor.h definitions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _ASM_UBICOM32_XOR_H ++#define _ASM_UBICOM32_XOR_H ++ ++#include <asm-generic/xor.h> ++ ++#endif /* _ASM_UBICOM32_XOR_H */ +--- /dev/null ++++ b/arch/ubicom32/Kconfig +@@ -0,0 +1,455 @@ ++# ++# For a description of the syntax of this configuration file, ++# see Documentation/kbuild/kconfig-language.txt. ++# ++ ++mainmenu "uClinux/ubicom32 (w/o MMU) Kernel Configuration" ++ ++config RAMBASE ++ hex ++ default "0x40000000" ++ help ++ Define the address that RAM starts at. ++ ++config UBICOM32 ++ bool ++ select HAVE_OPROFILE ++ default y ++ ++config RAMKERNEL ++ bool ++ default y ++ ++config CPU_BIG_ENDIAN ++ bool ++ default y ++ ++config FORCE_MAX_ZONEORDER ++ int ++ default "14" ++ ++config HAVE_CLK ++ bool ++ default y ++ ++config MMU ++ bool ++ default n ++ ++config FPU ++ bool ++ default n ++ ++config ZONE_DMA ++ bool ++ default y ++ ++config RWSEM_GENERIC_SPINLOCK ++ bool ++ default y ++ ++config RWSEM_XCHGADD_ALGORITHM ++ bool ++ default n ++ ++config ARCH_HAS_ILOG2_U32 ++ bool ++ default n ++ ++config ARCH_HAS_ILOG2_U64 ++ bool ++ default n ++ ++config GENERIC_FIND_NEXT_BIT ++ bool ++ default y ++ ++config GENERIC_GPIO ++ bool ++ default y ++ ++config GPIOLIB ++ bool ++ default y ++ ++config GENERIC_HWEIGHT ++ bool ++ default y ++ ++config GENERIC_HARDIRQS ++ bool ++ default y ++ ++config STACKTRACE_SUPPORT ++ bool ++ default y ++ ++config LOCKDEP_SUPPORT ++ bool ++ default y ++ ++config GENERIC_CALIBRATE_DELAY ++ bool ++ default y ++ ++config GENERIC_TIME ++ bool ++ default y ++ ++config TIME_LOW_RES ++ bool ++ default y ++ ++config GENERIC_CLOCKEVENTS ++ bool ++ default y ++ ++config GENERIC_CLOCKEVENTS_BROADCAST ++ bool ++ depends on GENERIC_CLOCKEVENTS ++ default y if SMP && !LOCAL_TIMERS ++ ++config NO_IOPORT ++ def_bool y ++ ++config ARCH_SUPPORTS_AOUT ++ def_bool y ++ ++config IRQ_PER_CPU ++ bool ++ default y ++ ++config SCHED_NO_NO_OMIT_FRAME_POINTER ++ bool ++ default y ++ ++menu "Processor type and features" ++ ++config BRD_32MB ++ bool ++ depends on IP5160EVAL ++ help ++ Board has 32MB of RAM on it. It is a hidden option used to select default for size of RAM ++ default n ++ ++config BRD_64MB ++ bool ++ depends on IP7145DPF || IP7160RGW || IP7160BRINGUP || IP7160DPF || IP5170DPF || IP5160DEV ++ help ++ Board has 64MB of RAM on it. It is a hidden option used to select default for size of RAM ++ default n ++ ++config BRD_128MB ++ bool ++ depends on IP7500MODULE || IP7500AV || IP7500MEDIA ++ help ++ Board has 128MB of RAM on it. It is a hidden option used to select default for size of RAM ++ default n ++ ++comment "Processor type will be selected by Board" ++ ++config UBICOM32_V3 ++ bool ++ help ++ Ubicom IP5xxx series processor support. ++ ++config UBICOM32_V4 ++ bool ++ help ++ Ubicom IP7xxx series processor support. ++ ++comment "Board" ++choice ++ prompt "Board type" ++ help ++ Select your board. ++ ++config NOBOARD ++ bool "No board selected" ++ help ++ Default. Don't select any board specific config. Will not build unless you change! ++ ++# Add your boards here ++source "arch/ubicom32/mach-ip5k/Kconfig" ++source "arch/ubicom32/mach-ip7k/Kconfig" ++ ++endchoice ++ ++comment "Kernel Options" ++config SMP ++ bool "Symmetric multi-processing support" ++ select USE_GENERIC_SMP_HELPERS ++ default n ++ help ++ Enables multithreading support. Enabling SMP support increases ++ the size of system data structures. SMP support can have either ++ positive or negative impact on performance depending on workloads. ++ ++ If you do not know what to do here, say N. ++ ++config NR_CPUS ++ int "Number of configured CPUs" ++ range 2 32 ++ default 2 ++ depends on SMP ++ help ++ Upper bound on the number of CPUs. Space is reserved ++ at compile time for this many CPUs. ++ ++config LOCAL_TIMERS ++ bool "Use local timer interrupts" ++ depends on SMP ++ default y ++ help ++ Enable support for local timers on SMP platforms, rather then the ++ legacy IPI broadcast method. Local timers allows the system ++ accounting to be spread across the timer interval, preventing a ++ "thundering herd" at every timer tick. A physical timer is allocated ++ per cpu. ++ ++config TIMER_EXTRA_ALLOC ++ int "Number of additional physical timer events to create" ++ depends on GENERIC_CLOCKEVENTS ++ default 0 ++ help ++ The Ubicom32 processor has a number of event timers that can be wrapped ++ in Linux clock event structures (assuming that the timers are not being ++ used for another purpose). Based on the value of LOCAL_TIMERS, either ++ 2 timers will be used or a timer will be used for every CPU. This value ++ allows the programmer to select additional timers over that amount. ++ ++config IRQSTACKS ++ bool "Create separate stacks for interrupt handling" ++ default n ++ help ++ Selecting this causes interrupts to be created on a separate ++ stack instead of nesting the interrupts on the kernel stack. ++ ++config IRQSTACKS_USEOCM ++ bool "Use OCM for interrupt stacks" ++ default n ++ depends on IRQSTACKS ++ help ++ Selecting this cause the interrupt stacks to be placed in OCM ++ reducing cache misses at the expense of using the OCM for servicing ++ interrupts. ++ ++menu "OCM Instruction Heap" ++ ++config OCM_MODULES_RESERVATION ++ int "OCM Instruction heap reservation. 0-192 kB" ++ range 0 192 ++ default "0" ++ help ++ The minimum amount of OCM memory to reserve for kernel loadable module ++ code. If you are not using this memory it cannot be used for anything ++ else. Leave it as 0 if you have prebuilt modules that are compiled with ++ OCM support. ++ ++config OCM_MODULES_MAY_CONSUME_REMAINING_CODESPACE ++ bool "Give all unused ocm code space to the ocm instruction heap." ++ default n ++ help ++ Allow the OCM instruction heap allocation to consume any remaining ++ unused OCM code space. The result of this is that you will not have ++ and deterministic results, but you will not have any waste either. ++ ++config OCM_MODULES_FALLBACK_TO_DDR ++ bool "Loadable Modules requiring OCM may fallback to use DDR." ++ default n ++ help ++ If a module cannot get the OCM code it requires allow DDR to ++ be used instead. ++endmenu ++ ++config HZ ++ int "Frequency of 'jiffies' (for polling)" ++ default 1000 ++ help ++ 100 is common for embedded systems, but 1000 allows ++ you to do more drivers without actually having ++ interrupts working properly. ++ ++comment "RAM configuration" ++ ++if BRD_32MB ++ ++config RAMSIZE ++ hex "Size of RAM (in bytes)" ++ range 0x00000000 0x02000000 ++ default "0x02000000" ++ help ++ Define the size of the system RAM. If you select 0 then the ++ kernel will try to probe the RAM size at runtime. This is not ++ supported on all CPU types. ++ ++endif ++ ++if BRD_64MB ++ ++config RAMSIZE ++ hex "Size of RAM (in bytes)" ++ range 0x00000000 0x04000000 ++ default "0x04000000" ++ help ++ Define the size of the system RAM. If you select 0 then the ++ kernel will try to probe the RAM size at runtime. This is not ++ supported on all CPU types. ++ ++endif ++ ++if BRD_128MB ++ ++config RAMSIZE ++ hex "Size of RAM (in bytes)" ++ range 0x00000000 0x08000000 ++ default "0x08000000" ++ help ++ Define the size of the system RAM. If you select 0 then the ++ kernel will try to probe the RAM size at runtime. This is not ++ supported on all CPU types. ++ ++endif ++ ++config KERNELBASE ++ hex "Address of the base of kernel code" ++ default "0x40400000" ++ help ++ For the time being we are going to start the Kernel at a 4 meg offset. ++ ++comment "Build options" ++config LINKER_RELAXATION ++ bool "Linker Relaxation" ++ default y ++ help ++ Turns on linker relaxation that will produce smaller ++ faster code. Increases link time. ++ ++comment "Driver options" ++menu "PCI Bus" ++config PCI ++ bool "PCI bus" ++ default true ++ help ++ Enable/Disable PCI bus ++ source "drivers/pci/Kconfig" ++ ++ ++config PCI_DEV0_IDSEL ++ hex "slot 0 address" ++ depends on PCI ++ default "0x01000000" ++ help ++ Slot 0 address. This address should correspond to the address line ++ which the IDSEL bit for this slot is connected to. ++ ++config PCI_DEV1_IDSEL ++ hex "slot 1 address" ++ depends on PCI ++ default "0x02000000" ++ help ++ Slot 1 address. This address should correspond to the address line ++ which the IDSEL bit for this slot is connected to. ++endmenu ++# End PCI ++ ++menu "Input devices" ++config UBICOM_INPUT ++ bool "Ubicom polled GPIO input driver" ++ select INPUT ++ select INPUT_POLLDEV ++ help ++ Polling input driver, much like the GPIO input driver, except that it doesn't ++ rely on interrupts. It will report events via the input subsystem. ++ default n ++ ++config UBICOM_INPUT_I2C ++ bool "Ubicom polled GPIO input driver over I2C" ++ select INPUT ++ select INPUT_POLLDEV ++ help ++ Polling input driver, much like the PCA953x driver, it can support a variety of ++ different I2C I/O expanders. This device polls the I2C I/O expander for events ++ and reports them via the input subsystem. ++ default n ++endmenu ++# Input devices ++ ++menu "Misc devices" ++config UBICOM_HID ++ bool "Ubicom HID driver" ++ select INPUT ++ select INPUT_POLLDEV ++ select LCD_CLASS_DEVICE ++ help ++ Driver for HID chip found on some Ubicom reference designs. This chip handles ++ PWM, button input, and IR remote control. It registers as an input device and ++ a backlight device. ++ default n ++endmenu ++# Misc devices ++ ++config CMDLINE_BOOL ++ bool "Built-in kernel command line" ++ default n ++ help ++ Allow for specifying boot arguments to the kernel at ++ build time. On some systems (e.g. embedded ones), it is ++ necessary or convenient to provide some or all of the ++ kernel boot arguments with the kernel itself (that is, ++ to not rely on the boot loader to provide them.) ++ ++ To compile command line arguments into the kernel, ++ set this option to 'Y', then fill in the ++ the boot arguments in CONFIG_CMDLINE. ++ ++ Systems with fully functional boot loaders (i.e. non-embedded) ++ should leave this option set to 'N'. ++ ++config CMDLINE ++ string "Built-in kernel command string" ++ depends on CMDLINE_BOOL ++ default "" ++ help ++ Enter arguments here that should be compiled into the kernel ++ image and used at boot time. If the boot loader provides a ++ command line at boot time, it is appended to this string to ++ form the full kernel command line, when the system boots. ++ ++ However, you can use the CONFIG_CMDLINE_OVERRIDE option to ++ change this behavior. ++ ++ In most cases, the command line (whether built-in or provided ++ by the boot loader) should specify the device for the root ++ file system. ++ ++config CMDLINE_OVERRIDE ++ bool "Built-in command line overrides boot loader arguments" ++ default n ++ depends on CMDLINE_BOOL ++ help ++ Set this option to 'Y' to have the kernel ignore the boot loader ++ command line, and use ONLY the built-in command line. ++ ++ This is used to work around broken boot loaders. This should ++ be set to 'N' under normal conditions. ++ ++endmenu ++# End Processor type and features ++ ++source "arch/ubicom32/Kconfig.debug" ++ ++menu "Executable file formats" ++source "fs/Kconfig.binfmt" ++endmenu ++ ++source "init/Kconfig" ++source "kernel/Kconfig.preempt" ++source "kernel/time/Kconfig" ++source "mm/Kconfig" ++source "net/Kconfig" ++source "drivers/Kconfig" ++source "fs/Kconfig" ++source "security/Kconfig" ++source "crypto/Kconfig" ++source "lib/Kconfig" +--- /dev/null ++++ b/arch/ubicom32/Kconfig.debug +@@ -0,0 +1,117 @@ ++menu "Kernel hacking" ++ ++config TRACE_IRQFLAGS_SUPPORT ++ def_bool y ++ ++config PROTECT_KERNEL ++ default y ++ bool 'Enable Kernel range register Protection' ++ help ++ Adds code to enable/disable range registers to protect static ++ kernel code/data from userspace. Currently the ranges covered ++ do no protect kernel loadable modules or dynamically allocated ++ kernel data. ++ ++config NO_KERNEL_MSG ++ bool "Suppress Kernel BUG Messages" ++ help ++ Do not output any debug BUG messages within the kernel. ++ ++config EARLY_PRINTK ++ bool "Use the driver that you selected as console also for early printk (to debug kernel bootup)." ++ default n ++ help ++ If you want to use the serdes driver (console=ttyUS0) for ++ early printk, you must also supply an additional kernel boot ++ parameter like this: ++ ++ serdes=ioportaddr,irq,clockrate,baud ++ ++ For an IP7160RGW eval board, you could use this: ++ ++ serdes=0x2004000,61,250000000,57600 ++ ++ which will let you see early printk output at 57600 baud. ++ ++config STOP_ON_TRAP ++ bool "Enable stopping at the LDSR for all traps" ++ default n ++ help ++ Cause the LDSR to stop all threads whenever a trap is about to be serviced ++ ++config STOP_ON_BUG ++ bool "Enable stopping on failed BUG_ON()" ++ default n ++ help ++ Cause all BUG_ON failures to stop all threads ++ ++config DEBUG_IRQMEASURE ++ bool "Enable IRQ handler measurements" ++ default n ++ help ++ When enabled each IRQ's min/avg/max times will be printed. If the handler ++ re-enables interrupt, the times will show the full time including to service ++ nested interrupts. See /proc/irq_measurements. ++ ++config DEBUG_PCIMEASURE ++ bool "Enable PCI transaction measurements" ++ default n ++ help ++ When enabled the system will measure the min/avg/max timer for each PCI transactions. ++ See /proc/pci_measurements. ++ ++config ACCESS_OK_CHECKS_ENABLED ++ bool "Enable user space access checks" ++ default n ++ help ++ Enabling this check causes the kernel to verify that addresses passed ++ to the kernel by the user space code are within the processes ++ address space. On a no-mmu system, this is done by examining the ++ processes memory data structures (adversly affecting performance) but ++ ensuring that a process does not ask the kernel to violate another ++ processes address space. Sadly, the kernel uses access_ok() for ++ address that are in the kernel which results in a large volume of ++ false positives. ++ ++choice ++ prompt "Unaligned Access Support" ++ default UNALIGNED_ACCESS_ENABLED ++ help ++ Kernel / Userspace unaligned access handling. ++ ++config UNALIGNED_ACCESS_ENABLED ++ bool "Kernel and Userspace" ++ help ++ ++config UNALIGNED_ACCESS_USERSPACE_ONLY ++ bool "Userspace Only" ++ help ++ ++config UNALIGNED_ACCESS_DISABLED ++ bool "Disabled" ++ help ++ ++endchoice ++ ++config DEBUG_STACKOVERFLOW ++ bool "Check for stack overflows" ++ default n ++ depends on DEBUG_KERNEL ++ help ++ This option will cause messages to be printed if free kernel stack space ++ drops below a certain limit (THREAD_SIZE /8). ++ ++config DEBUG_STACK_USAGE ++ bool "Stack utilization instrumentation" ++ default n ++ depends on DEBUG_KERNEL ++ help ++ Enables the display of the minimum amount of free kernel stack which each ++ task has ever had available in the sysrq-T and sysrq-P debug output. ++ ++ This option will slow down process creation somewhat. ++ ++source "lib/Kconfig.debug" ++ ++endmenu ++ +--- /dev/null ++++ b/arch/ubicom32/kernel/asm-offsets.c +@@ -0,0 +1,162 @@ ++/* ++ * arch/ubicom32/kernel/asm-offsets.c ++ * Ubicom32 architecture definitions needed by assembly language modules. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++/* ++ * This program is used to generate definitions needed by ++ * assembly language modules. ++ * ++ * We use the technique used in the OSF Mach kernel code: ++ * generate asm statements containing #defines, ++ * compile this file to assembler, and then extract the ++ * #defines from the assembly-language output. ++ */ ++ ++#include <linux/module.h> ++#include <linux/stddef.h> ++#include <linux/sched.h> ++#include <linux/kernel_stat.h> ++#include <linux/ptrace.h> ++#include <linux/hardirq.h> ++#include <asm/bootinfo.h> ++#include <asm/irq.h> ++#include <asm/thread_info.h> ++ ++#define DEFINE(sym, val) \ ++ asm volatile("\n->" #sym " %0 " #val : : "i" (val)) ++ ++#define BLANK() asm volatile("\n->" : : ) ++ ++int main(void) ++{ ++ /* offsets into the task struct */ ++ DEFINE(TASK_STATE, offsetof(struct task_struct, state)); ++ DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); ++ DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); ++ DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); ++ DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); ++ DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack)); ++ DEFINE(TASK_MM, offsetof(struct task_struct, mm)); ++ DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); ++ ++ /* offsets into the kernel_stat struct */ ++ DEFINE(STAT_IRQ, offsetof(struct kernel_stat, irqs)); ++ ++ /* offsets into the irq_cpustat_t struct */ ++ DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); ++ ++ /* offsets into the thread struct */ ++ DEFINE(THREAD_D10, offsetof(struct thread_struct, d10)); ++ DEFINE(THREAD_D11, offsetof(struct thread_struct, d11)); ++ DEFINE(THREAD_D12, offsetof(struct thread_struct, d12)); ++ DEFINE(THREAD_D13, offsetof(struct thread_struct, d13)); ++ DEFINE(THREAD_A1, offsetof(struct thread_struct, a1)); ++ DEFINE(THREAD_A2, offsetof(struct thread_struct, a2)); ++ DEFINE(THREAD_A5, offsetof(struct thread_struct, a5)); ++ DEFINE(THREAD_A6, offsetof(struct thread_struct, a6)); ++ DEFINE(THREAD_SP, offsetof(struct thread_struct, sp)); ++ ++ /* offsets into the pt_regs */ ++ DEFINE(PT_D0, offsetof(struct pt_regs, dn[0])); ++ DEFINE(PT_D1, offsetof(struct pt_regs, dn[1])); ++ DEFINE(PT_D2, offsetof(struct pt_regs, dn[2])); ++ DEFINE(PT_D3, offsetof(struct pt_regs, dn[3])); ++ DEFINE(PT_D4, offsetof(struct pt_regs, dn[4])); ++ DEFINE(PT_D5, offsetof(struct pt_regs, dn[5])); ++ DEFINE(PT_D6, offsetof(struct pt_regs, dn[6])); ++ DEFINE(PT_D7, offsetof(struct pt_regs, dn[7])); ++ DEFINE(PT_D8, offsetof(struct pt_regs, dn[8])); ++ DEFINE(PT_D9, offsetof(struct pt_regs, dn[9])); ++ DEFINE(PT_D10, offsetof(struct pt_regs, dn[10])); ++ DEFINE(PT_D11, offsetof(struct pt_regs, dn[11])); ++ DEFINE(PT_D12, offsetof(struct pt_regs, dn[12])); ++ DEFINE(PT_D13, offsetof(struct pt_regs, dn[13])); ++ DEFINE(PT_D14, offsetof(struct pt_regs, dn[14])); ++ DEFINE(PT_D15, offsetof(struct pt_regs, dn[15])); ++ DEFINE(PT_A0, offsetof(struct pt_regs, an[0])); ++ DEFINE(PT_A1, offsetof(struct pt_regs, an[1])); ++ DEFINE(PT_A2, offsetof(struct pt_regs, an[2])); ++ DEFINE(PT_A3, offsetof(struct pt_regs, an[3])); ++ DEFINE(PT_A4, offsetof(struct pt_regs, an[4])); ++ DEFINE(PT_A5, offsetof(struct pt_regs, an[5])); ++ DEFINE(PT_A6, offsetof(struct pt_regs, an[6])); ++ DEFINE(PT_A7, offsetof(struct pt_regs, an[7])); ++ DEFINE(PT_SP, offsetof(struct pt_regs, an[7])); ++ ++ DEFINE(PT_ACC0HI, offsetof(struct pt_regs, acc0[0])); ++ DEFINE(PT_ACC0LO, offsetof(struct pt_regs, acc0[1])); ++ DEFINE(PT_MAC_RC16, offsetof(struct pt_regs, mac_rc16)); ++ ++ DEFINE(PT_ACC1HI, offsetof(struct pt_regs, acc1[0])); ++ DEFINE(PT_ACC1LO, offsetof(struct pt_regs, acc1[1])); ++ ++ DEFINE(PT_SOURCE3, offsetof(struct pt_regs, source3)); ++ DEFINE(PT_INST_CNT, offsetof(struct pt_regs, inst_cnt)); ++ DEFINE(PT_CSR, offsetof(struct pt_regs, csr)); ++ DEFINE(PT_DUMMY_UNUSED, offsetof(struct pt_regs, dummy_unused)); ++ ++ DEFINE(PT_INT_MASK0, offsetof(struct pt_regs, int_mask0)); ++ DEFINE(PT_INT_MASK1, offsetof(struct pt_regs, int_mask1)); ++ ++ DEFINE(PT_PC, offsetof(struct pt_regs, pc)); ++ ++ DEFINE(PT_TRAP_CAUSE, offsetof(struct pt_regs, trap_cause)); ++ ++ DEFINE(PT_SIZE, sizeof(struct pt_regs)); ++ ++ DEFINE(PT_FRAME_TYPE, offsetof(struct pt_regs, frame_type)); ++ ++ DEFINE(PT_ORIGINAL_D0, offsetof(struct pt_regs, original_dn_0)); ++ DEFINE(PT_PREVIOUS_PC, offsetof(struct pt_regs, previous_pc)); ++ ++ /* offsets into the kernel_stat struct */ ++ DEFINE(STAT_IRQ, offsetof(struct kernel_stat, irqs)); ++ ++ /* signal defines */ ++ DEFINE(SIGSEGV, SIGSEGV); ++ //DEFINE(SEGV_MAPERR, SEGV_MAPERR); ++ DEFINE(SIGTRAP, SIGTRAP); ++ //DEFINE(TRAP_TRACE, TRAP_TRACE); ++ ++ DEFINE(PT_PTRACED, PT_PTRACED); ++ DEFINE(PT_DTRACE, PT_DTRACE); ++ ++ DEFINE(ASM_THREAD_SIZE, THREAD_SIZE); ++ ++ /* Offsets in thread_info structure */ ++ DEFINE(TI_TASK, offsetof(struct thread_info, task)); ++ DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain)); ++ DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); ++ DEFINE(TI_PREEMPTCOUNT, offsetof(struct thread_info, preempt_count)); ++ DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); ++ DEFINE(TI_INTR_NESTING, offsetof(struct thread_info, interrupt_nesting)); ++ DEFINE(ASM_TIF_NEED_RESCHED, TIF_NEED_RESCHED); ++ DEFINE(ASM_TIF_SYSCALL_TRACE, TIF_SYSCALL_TRACE); ++ DEFINE(ASM_TIF_SIGPENDING, TIF_SIGPENDING); ++ ++ DEFINE(ASM_RAM_END, (CONFIG_RAMBASE + CONFIG_RAMSIZE)); ++ return 0; ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/devtree.c +@@ -0,0 +1,173 @@ ++/* ++ * arch/ubicom32/kernel/devtree.c ++ * Ubicom32 architecture device tree implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/string.h> ++#include <linux/errno.h> ++#include <asm/devtree.h> ++ ++/* ++ * The device tree. ++ */ ++struct devtree_node *devtree; ++ ++/* ++ * devtree_print() ++ * Print the device tree. ++ */ ++void devtree_print(void) ++{ ++ struct devtree_node *p = devtree; ++ printk(KERN_INFO "Device Tree:\n"); ++ while (p) { ++ if (p->magic != DEVTREE_NODE_MAGIC) { ++ printk(KERN_EMERG ++ "device tree has improper node: %p\n", p); ++ return; ++ } ++ printk(KERN_INFO "\t%p: sendirq=%03d, recvirq=%03d, " ++ " name=%s\n", p, p->sendirq, p->recvirq, p->name); ++ p = p->next; ++ } ++} ++EXPORT_SYMBOL(devtree_print); ++ ++/* ++ * devtree_irq() ++ * Return the IRQ(s) associated with devtree node. ++ */ ++int devtree_irq(struct devtree_node *dn, ++ unsigned char *sendirq, ++ unsigned char *recvirq) ++{ ++ if (dn->magic != DEVTREE_NODE_MAGIC) { ++ printk(KERN_EMERG "improper node: %p\n", dn); ++ if (sendirq) { ++ *sendirq = DEVTREE_IRQ_NONE; ++ } ++ if (recvirq) { ++ *recvirq = DEVTREE_IRQ_NONE; ++ } ++ return -EFAULT; ++ } ++ ++ /* ++ * Copy the devtree irq(s) to the output parameters. ++ */ ++ if (sendirq) { ++ *sendirq = dn->sendirq; ++ } ++ if (recvirq) { ++ *recvirq = dn->recvirq; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(devtree_irq); ++ ++/* ++ * devtree_find_next() ++ * Provide an iterator for walking the device tree. ++ */ ++struct devtree_node *devtree_find_next(struct devtree_node **cur) ++{ ++ struct devtree_node *p = *cur; ++ if (!p) { ++ *cur = devtree; ++ return devtree; ++ } ++ p = p->next; ++ *cur = p; ++ return p; ++} ++ ++/* ++ * devtree_find_by_irq() ++ * Return the node associated with a given irq. ++ */ ++struct devtree_node *devtree_find_by_irq(uint8_t sendirq, uint8_t recvirq) ++{ ++ struct devtree_node *p = devtree; ++ ++ if (sendirq == recvirq) { ++ printk(KERN_EMERG "identical request makes no sense sendirq = " ++ "%d, recvirq= %d\n", sendirq, recvirq); ++ return NULL; ++ } ++ ++ while (p) { ++ if (p->magic != DEVTREE_NODE_MAGIC) { ++ printk(KERN_EMERG ++ "device tree has improper node: %p\n", p); ++ return NULL; ++ } ++ ++ /* ++ * See if we can find a match on the IRQ(s) specified. ++ */ ++ if ((sendirq == p->sendirq) && (recvirq == p->recvirq)) { ++ return p; ++ } ++ ++ if ((sendirq == DEVTREE_IRQ_DONTCARE) && ++ (p->recvirq == recvirq)) { ++ return p; ++ } ++ ++ if ((recvirq == DEVTREE_IRQ_DONTCARE) && ++ (p->sendirq == sendirq)) { ++ return p; ++ } ++ ++ p = p->next; ++ } ++ return NULL; ++} ++EXPORT_SYMBOL(devtree_find_by_irq); ++ ++/* ++ * devtree_find_node() ++ * Find a node in the device tree by name. ++ */ ++struct devtree_node *devtree_find_node(const char *str) ++{ ++ struct devtree_node *p = devtree; ++ while (p) { ++ if (p->magic != DEVTREE_NODE_MAGIC) { ++ printk(KERN_EMERG ++ "device tree has improper node: %p\n", p); ++ return NULL; ++ } ++ if (strcmp(p->name, str) == 0) { ++ return p; ++ } ++ p = p->next; ++ } ++ return NULL; ++} ++EXPORT_SYMBOL(devtree_find_node); +--- /dev/null ++++ b/arch/ubicom32/kernel/dma.c +@@ -0,0 +1,60 @@ ++/* ++ * arch/ubicom32/kernel/dma.c ++ * Ubicom32 architecture dynamic DMA mapping support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * ++ * We never have any address translations to worry about, so this ++ * is just alloc/free. ++ */ ++ ++#include <linux/types.h> ++#include <linux/mm.h> ++#include <linux/string.h> ++#include <linux/device.h> ++#include <linux/io.h> ++ ++void *dma_alloc_coherent(struct device *dev, size_t size, ++ dma_addr_t *dma_handle, int gfp) ++{ ++ void *ret; ++ /* ignore region specifiers */ ++ gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); ++ ++ if (dev == NULL || (*dev->dma_mask < 0xffffffff)) ++ gfp |= GFP_DMA; ++ ret = (void *)__get_free_pages(gfp, get_order(size)); ++ ++ if (ret != NULL) { ++ memset(ret, 0, size); ++ *dma_handle = virt_to_phys(ret); ++ } ++ return ret; ++} ++ ++void dma_free_coherent(struct device *dev, size_t size, ++ void *vaddr, dma_addr_t dma_handle) ++{ ++ free_pages((unsigned long)vaddr, get_order(size)); ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/flat.c +@@ -0,0 +1,206 @@ ++/* ++ * arch/ubicom32/kernel/flat.c ++ * Ubicom32 architecture flat executable format support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/flat.h> ++ ++unsigned long ubicom32_flat_get_addr_from_rp(unsigned long *rp, ++ u32_t relval, ++ u32_t flags, ++ unsigned long *persistent) ++{ ++ u32_t relval_reloc_type = relval >> 27; ++ u32_t insn = *rp; ++ ++ if (*persistent) { ++ /* ++ * relval holds the relocation that has to be adjusted. ++ */ ++ if (relval == 0) { ++ *persistent = 0; ++ } ++ ++ return relval; ++ } ++ ++ if (relval_reloc_type == R_UBICOM32_32) { ++ /* ++ * insn holds the relocation ++ */ ++ return insn; ++ } ++ ++ /* ++ * We don't know this one. ++ */ ++ return 0; ++} ++ ++void ubicom32_flat_put_addr_at_rp(unsigned long *rp, ++ u32_t val, ++ u32_t relval, ++ unsigned long *persistent) ++{ ++ u32_t reloc_type = (relval >> 27) & 0x1f; ++ u32_t insn = *rp; ++ ++ /* ++ * If persistent is set then it contains the relocation type. ++ */ ++ if (*persistent) { ++ /* ++ * If persistent is set then it contains the relocation type. ++ */ ++ reloc_type = (*persistent >> 27) & 0x1f; ++ } ++ ++ switch (reloc_type) { ++ case R_UBICOM32_32: ++ /* ++ * Store the 32 bits as is. ++ */ ++ *rp = val; ++ break; ++ case R_UBICOM32_HI24: ++ { ++ /* ++ * 24 bit relocation that is part of the MOVEAI ++ * instruction. The 24 bits come from bits 7 - 30 of the ++ * relocation. The 24 bits eventually get split into 2 ++ * fields in the instruction encoding. ++ * ++ * - Bits 7 - 27 of the relocation are encoded into bits ++ * 0 - 20 of the instruction. ++ * ++ * - Bits 28 - 30 of the relocation are encoded into bit ++ * 24 - 26 of the instruction. ++ */ ++ u32_t mask = 0x1fffff | (0x7 << 24); ++ u32_t valid24bits = (val >> 7) & 0xffffff; ++ u32_t bot_21 = valid24bits & 0x1fffff; ++ u32_t upper_3_bits = ((valid24bits & 0xe00000) << 3); ++ insn &= ~mask; ++ ++ insn |= bot_21; ++ insn |= upper_3_bits; ++ *rp = insn; ++ } ++ break; ++ case R_UBICOM32_LO7_S: ++ case R_UBICOM32_LO7_2_S: ++ case R_UBICOM32_LO7_4_S: ++ { ++ /* ++ * Bits 0 - 6 of the relocation are encoded into the ++ * 7bit unsigned immediate fields of the SOURCE-1 field ++ * of the instruction. The immediate value is left ++ * shifted by (0, 1, 2) based on the operand size. ++ */ ++ u32_t mask = 0x1f | (0x3 << 8); ++ u32_t bottom, top; ++ val &= 0x7f; ++ if (reloc_type == R_UBICOM32_LO7_2_S) { ++ val >>= 1; ++ } else if (reloc_type == R_UBICOM32_LO7_4_S) { ++ val >>= 2; ++ } ++ ++ bottom = val & 0x1f; ++ top = val >> 5; ++ insn &= ~mask; ++ insn |= bottom; ++ insn |= (top << 8); ++ BUG_ON(*rp != insn); ++ *rp = insn; ++ break; ++ } ++ case R_UBICOM32_LO7_D: ++ case R_UBICOM32_LO7_2_D: ++ case R_UBICOM32_LO7_4_D: ++ { ++ /* ++ * Bits 0 - 6 of the relocation are encoded into the ++ * 7bit unsigned immediate fields of the DESTINATION ++ * field of the instruction. The immediate value is ++ * left shifted by (0, 1, 2) based on the operand size. ++ */ ++ u32_t mask = (0x1f | (0x3 << 8)) << 16; ++ u32_t bottom, top; ++ val &= 0x7f; ++ if (reloc_type == R_UBICOM32_LO7_2_D) { ++ val >>= 1; ++ } else if (reloc_type == R_UBICOM32_LO7_4_D) { ++ val >>= 2; ++ } ++ bottom = (val & 0x1f) << 16; ++ top = (val >> 5) << 16; ++ insn &= ~mask; ++ insn |= bottom; ++ insn |= (top << 8); ++ BUG_ON(*rp != insn); ++ *rp = insn; ++ break; ++ } ++ case R_UBICOM32_LO7_CALLI: ++ case R_UBICOM32_LO16_CALLI: ++ { ++ /* ++ * Extract the offset for a CALLI instruction. The ++ * offsets can be either 7 bits or 18 bits. Since all ++ * instructions in ubicom32 architecture are at work ++ * aligned addresses the truncated offset is right ++ * shifted by 2 before being encoded in the instruction. ++ */ ++ if (reloc_type == R_UBICOM32_LO7_CALLI) { ++ val &= 0x7f; ++ } else { ++ val &= 0x3ffff; ++ } ++ ++ val >>= 2; ++ ++ insn &= ~0x071f071f; ++ insn |= (val & 0x1f) << 0; ++ val >>= 5; ++ insn |= (val & 0x07) << 8; ++ val >>= 3; ++ insn |= (val & 0x1f) << 16; ++ val >>= 5; ++ insn |= (val & 0x07) << 24; ++ if (reloc_type == R_UBICOM32_LO7_CALLI) { ++ BUG_ON(*rp != insn); ++ } ++ *rp = insn; ++ } ++ break; ++ } ++ ++ if (*persistent) { ++ *persistent = 0; ++ } ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/head.S +@@ -0,0 +1,247 @@ ++/* ++ * arch/ubicom32/kernel/head.S ++ * <TODO: Replace with short file description> ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/sys.h> ++#include <linux/linkage.h> ++#include <asm/asm-offsets.h> ++#include <asm/page_offset.h> ++#define __ASM__ ++#include <asm/ip5000.h> ++ ++ ++#define SRC_AN A3 ++#define DST_AN A4 ++ ++#define PARAM_DN D0 ++#define TMP_DN D15 ++#define TMP2_DN D14 ++ ++/* ++ * The following code is placed at the start of the Linux section of memory. ++ * This is the primary entry point for Linux. ++ * ++ * However, we also want the syscall entry/exit code to be at a fixed address. ++ * So we take the primary entry point and reserve 16 bytes. That address is ++ * where the system_call entry point exists. This 16 bytes basically allows ++ * us to jump around the system_call entry point code to the actual startup ++ * code. ++ * ++ * Linux Memory Map (see vlinux.lds.S): ++ * 0x40400000 - Primary Entry Point for Linux (jump around code below). ++ * 0x40400010 - Syscall Entry Point. ++ */ ++ ++ .sect .skip_syscall, "ax", @progbits ++ .global __skip_syscall_section ++__skip_syscall_section: ++ moveai A3, #%hi(_start) ++ lea.1 A3, %lo(_start)(A3) ++ ret A3 ++/* ++ * __os_node_offset contains the offset from KERNELBASE to the os_node, it is ++ * not intended to be used by anything except the boot code. ++ */ ++__os_node_offset: ++.long (_os_node - CONFIG_KERNELBASE) ++ ++.text ++.global _start ++ ++/* ++ * start() ++ * This is the start of the Linux kernel. ++ */ ++_start: ++ move.4 SCRATCHPAD1, #0 ++ ++ ++/* ++ * Setup the range registers... the loader has setup a few, but we will go ahead ++ * and correct them for our own limits. Note that once set these are never ++ * changed again. The ranges are as follows ++ * ++ * D_RANGE0 - io block (set up by loaded) ++ * ++ * I_RANGE0 and D_RANGE1 - kernel/ultra loader address space bottom of ocm-> top ++ * of ram typically 0x3ffc0000 - 0x440000000 ++ * I_RANGE1 - kernel / userspace transition area (aka syscalls, context switches) ++ * typically 0x40400000 - ~0x40400400 ++ * I_RANGE2 / D_RANGE2 - slab area ++ * typically 0x40A00000 - ~0x44000000 ++ * ++ * I_RANGE4, D_RANGE3, D_RANGE3 - unused. ++ */ ++ moveai SRC_AN, #%hi(PAGE_OFFSET_RAW) ++ lea.4 SRC_AN, %lo(PAGE_OFFSET_RAW)(SRC_AN) ++ move.4 D_RANGE1_LO, SRC_AN ++ move.4 I_RANGE0_LO, SRC_AN ++ ++; don't try to calculate I_RANGE_HI, see below ++; moveai SRC_AN, #%hi(___init_end-4) ++; lea.4 SRC_AN, %lo(___init_end-4)(SRC_AN) ++; move.4 I_RANGE0_HI, SRC_AN ++ ++ moveai SRC_AN, #%hi(CONFIG_RAMBASE+CONFIG_RAMSIZE-4) ++ lea.4 SRC_AN, %lo(CONFIG_RAMBASE+CONFIG_RAMSIZE-4)(SRC_AN) ++ move.4 D_RANGE1_HI, SRC_AN ++ ++; for now allow the whole ram to be executable as well so we don't run into problems ++; once we load user more code. ++ move.4 I_RANGE0_HI, SRC_AN ++ ++#ifdef CONFIG_PROTECT_KERNEL ++; when kernel protection is enabled, we only open up syscall and non kernel text ++; for userspace apps, for now only irange registers registers 1 and 2 are used for userspace. ++ ++ ;; syscall range ++ moveai SRC_AN, #%hi(_begin) ++ lea.4 SRC_AN, %lo(_begin)(SRC_AN) ++ move.4 I_RANGE1_LO, SRC_AN ++ moveai SRC_AN, #%hi(__fixed_text_end) ++ lea.4 SRC_AN, %lo(__fixed_text_end)(SRC_AN) ++ move.4 I_RANGE1_HI, SRC_AN ++ ++ ;; slab instructions ++ moveai SRC_AN, #%hi(_edata) ++ lea.4 SRC_AN, %lo(_edata)(SRC_AN) ++ move.4 I_RANGE2_LO, SRC_AN ++ ;; End of DDR is already in range0 hi so just copy it. ++ move.4 I_RANGE2_HI, I_RANGE0_HI ++ ++ ;; slab data (same as slab instructions but starting a little earlier). ++ moveai SRC_AN, #%hi(_data_protection_end) ++ lea.4 SRC_AN, %lo(_data_protection_end)(SRC_AN) ++ move.4 D_RANGE2_LO, SRC_AN ++ move.4 D_RANGE2_HI, I_RANGE0_HI ++ ++;; enable ranges ++ ;; skip I_RANGE0_EN ++ move.4 I_RANGE1_EN, #-1 ++ move.4 I_RANGE2_EN, #-1 ++ move.4 I_RANGE3_EN, #0 ++ ++ ;; skip D_RANGE0_EN or D_RANGE1_EN ++ move.4 D_RANGE2_EN, #-1 ++ move.4 D_RANGE3_EN, #0 ++ move.4 D_RANGE4_EN, #0 ++#endif ++ ++; ++; If __ocm_free_begin is smaller than __ocm_free_end the ++; setup OCM text and data ram banks properly ++; ++ moveai DST_AN, #%hi(__ocm_free_begin) ++ lea.4 TMP_DN, %lo(__ocm_free_begin)(DST_AN) ++ moveai DST_AN, #%hi(__ocm_free_end) ++ lea.4 TMP2_DN, %lo(__ocm_free_end)(DST_AN) ++ sub.4 #0, TMP2_DN, TMP_DN ++ jmple.f 2f ++ moveai DST_AN, #%hi(__data_begin) ++ lea.4 TMP_DN, %lo(__data_begin)(DST_AN) ++ moveai DST_AN, #%hi(OCMSTART) ++ lea.4 TMP2_DN, %lo(OCMSTART)(DST_AN) ++ sub.4 TMP_DN, TMP_DN, TMP2_DN ++ lsr.4 TMP_DN, TMP_DN, #15 ++ lsl.4 TMP_DN, #1, TMP_DN ++ moveai DST_AN, #%hi(OCMC_BASE) ++ add.4 OCMC_BANK_MASK(DST_AN), #-1, TMP_DN ++ pipe_flush 0 ++2: ++; ++; Load .ocm_text ++; ++ moveai DST_AN, #%hi(__ocm_text_run_end) ++ lea.4 TMP_DN, %lo(__ocm_text_run_end)(DST_AN) ++ moveai DST_AN, #%hi(__ocm_text_run_begin) ++ lea.4 DST_AN, %lo(__ocm_text_run_begin)(DST_AN) ++ moveai SRC_AN, #%hi(__ocm_text_load_begin) ++ lea.4 SRC_AN, %lo(__ocm_text_load_begin)(SRC_AN) ++ jmpt.t 2f ++ ++1: move.4 (DST_AN)4++, (SRC_AN)4++ ++ ++2: sub.4 #0, DST_AN, TMP_DN ++ jmpne.t 1b ++ ++; ++; Load .ocm_data ++; ++ moveai DST_AN, #%hi(__ocm_data_run_end) ++ lea.4 TMP_DN, %lo(__ocm_data_run_end)(DST_AN) ++ moveai DST_AN, #%hi(__ocm_data_run_begin) ++ lea.4 DST_AN, %lo(__ocm_data_run_begin)(DST_AN) ++ moveai SRC_AN, #%hi(__ocm_data_load_begin) ++ lea.4 SRC_AN, %lo(__ocm_data_load_begin)(SRC_AN) ++ jmpt.t 2f ++ ++1: move.4 (DST_AN)4++, (SRC_AN)4++ ++ ++2: sub.4 #0, DST_AN, TMP_DN ++ jmpne.t 1b ++ ++; Clear .bss ++; ++ moveai SRC_AN, #%hi(_ebss) ++ lea.4 TMP_DN, %lo(_ebss)(SRC_AN) ++ moveai DST_AN, #%hi(_sbss) ++ lea.4 DST_AN, %lo(_sbss)(DST_AN) ++ jmpt.t 2f ++ ++1: move.4 (DST_AN)4++, #0 ++ ++2: sub.4 #0, DST_AN, TMP_DN ++ jmpne.t 1b ++ ++; save our parameter to devtree (after clearing .bss) ++ moveai DST_AN, #%hi(devtree) ++ lea.4 DST_AN, %lo(devtree)(DST_AN) ++ move.4 (DST_AN), PARAM_DN ++ ++ moveai sp, #%hi(init_thread_union) ++ lea.4 sp, %lo(init_thread_union)(sp) ++ movei TMP_DN, #ASM_THREAD_SIZE ++ add.4 sp, sp, TMP_DN ++ move.4 -4(sp)++, #0 ; nesting level = 0 ++ move.4 -4(sp)++, #1 ; KERNEL_THREAD ++ ++;; ip3k-elf-gdb backend now sets scratchpad3 to 1 when either continue ++;; or single step commands are issued. scratchpad3 is set to 0 when the ++;; debugger detaches from the board. ++ move.4 TMP_DN, scratchpad3 ++ lsl.4 TMP_DN, TMP_DN, #0x0 ++ jmpeq.f _jump_to_start_kernel ++_ok_to_set_break_points_in_linux: ++;; THREAD_STALL ++ move.4 mt_dbg_active_clr,#-1 ++;; stalling the threads isn't instantaneous.. need to flush the pipe. ++ pipe_flush 0 ++ pipe_flush 0 ++ ++_jump_to_start_kernel: ++ moveai SRC_AN, #%hi(start_kernel) ++ lea.4 SRC_AN, %lo(start_kernel)(SRC_AN) ++ ret SRC_AN +--- /dev/null ++++ b/arch/ubicom32/kernel/init_task.c +@@ -0,0 +1,62 @@ ++/* ++ * arch/ubicom32/kernel/init_task.c ++ * Ubicom32 architecture task initialization implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/init_task.h> ++#include <linux/fs.h> ++#include <linux/mqueue.h> ++#include <linux/uaccess.h> ++#include <asm/pgtable.h> ++ ++static struct fs_struct init_fs = INIT_FS; ++static struct signal_struct init_signals = INIT_SIGNALS(init_signals); ++static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); ++struct mm_struct init_mm = INIT_MM(init_mm); ++EXPORT_SYMBOL(init_mm); ++ ++/* ++ * Initial task structure. ++ * ++ * All other task structs will be allocated on slabs in fork.c ++ */ ++struct task_struct init_task = INIT_TASK(init_task); ++ ++EXPORT_SYMBOL(init_task); ++ ++/* ++ * Initial thread structure. ++ * ++ * We need to make sure that this is 8192-byte aligned due to the ++ * way process stacks are handled. This is done by having a special ++ * "init_task" linker map entry.. ++ */ ++union thread_union init_thread_union ++ __attribute__((__section__(".data.init_task"))) = ++ { INIT_THREAD_INFO(init_task) }; +--- /dev/null ++++ b/arch/ubicom32/kernel/irq.c +@@ -0,0 +1,596 @@ ++/* ++ * arch/ubicom32/kernel/irq.c ++ * Ubicom32 architecture IRQ support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * (C) Copyright 2007, Greg Ungerer <gerg@snapgear.com> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/types.h> ++#include <linux/irq.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/kernel_stat.h> ++#include <linux/module.h> ++#include <linux/seq_file.h> ++#include <linux/proc_fs.h> ++#include <asm/system.h> ++#include <asm/traps.h> ++#include <asm/ldsr.h> ++#include <asm/ip5000.h> ++#include <asm/machdep.h> ++#include <asm/asm-offsets.h> ++#include <asm/thread.h> ++#include <asm/devtree.h> ++ ++unsigned int irq_soft_avail; ++static struct irqaction ubicom32_reserve_action[NR_IRQS]; ++ ++#if !defined(CONFIG_DEBUG_IRQMEASURE) ++#define IRQ_DECLARE_MEASUREMENT ++#define IRQ_MEASUREMENT_START() ++#define IRQ_MEASUREMENT_END(irq) ++#else ++#define IRQ_DECLARE_MEASUREMENT \ ++ int __diff; \ ++ unsigned int __tstart; ++ ++#define IRQ_MEASUREMENT_START() \ ++ __tstart = UBICOM32_IO_TIMER->sysval; ++ ++#define IRQ_MEASUREMENT_END(irq) \ ++ __diff = (int)UBICOM32_IO_TIMER->sysval - (int)__tstart; \ ++ irq_measurement_update((irq), __diff); ++ ++/* ++ * We keep track of the time spent in both irq_enter() ++ * and irq_exit(). ++ */ ++#define IRQ_WEIGHT 32 ++ ++struct irq_measurement { ++ volatile unsigned int min; ++ volatile unsigned int avg; ++ volatile unsigned int max; ++}; ++ ++static DEFINE_SPINLOCK(irq_measurement_lock); ++ ++/* ++ * Add 1 in for softirq (irq_exit()); ++ */ ++static struct irq_measurement irq_measurements[NR_IRQS + 1]; ++ ++/* ++ * irq_measurement_update() ++ * Update an entry in the measurement array for this irq. ++ */ ++static void irq_measurement_update(int irq, int sample) ++{ ++ struct irq_measurement *im = &irq_measurements[irq]; ++ spin_lock(&irq_measurement_lock); ++ if ((im->min == 0) || (im->min > sample)) { ++ im->min = sample; ++ } ++ if (im->max < sample) { ++ im->max = sample; ++ } ++ im->avg = ((im->avg * (IRQ_WEIGHT - 1)) + sample) / IRQ_WEIGHT; ++ spin_unlock(&irq_measurement_lock); ++} ++#endif ++ ++/* ++ * irq_kernel_stack_check() ++ * See if the kernel stack is within STACK_WARN of the end. ++ */ ++static void irq_kernel_stack_check(int irq, struct pt_regs *regs) ++{ ++#ifdef CONFIG_DEBUG_STACKOVERFLOW ++ unsigned long sp; ++ ++ /* ++ * Make sure that we are not close to the top of the stack and thus ++ * can not really service this interrupt. ++ */ ++ asm volatile ( ++ "and.4 %0, SP, %1 \n\t" ++ : "=d" (sp) ++ : "d" (THREAD_SIZE - 1) ++ : "cc" ++ ); ++ ++ if (sp < (sizeof(struct thread_info) + STACK_WARN)) { ++ printk(KERN_WARNING ++ "cpu[%d]: possible overflow detected sp remain: %p, " ++ "irq: %d, regs: %p\n", ++ thread_get_self(), (void *)sp, irq, regs); ++ dump_stack(); ++ } ++ ++ if (sp < (sizeof(struct thread_info) + 16)) { ++ THREAD_STALL; ++ } ++#endif ++} ++ ++/* ++ * irq_get_lsb() ++ * Get the LSB set in value ++ */ ++static int irq_get_lsb(unsigned int value) ++{ ++ static unsigned char irq_bits[8] = { ++ 3, 0, 1, 0, 2, 0, 1, 0 ++ }; ++ u32_t nextbit = 0; ++ ++ value = (value >> nextbit) | (value << ((sizeof(value) * 8) - nextbit)); ++ ++ /* ++ * It's unlikely that we find that we execute the body of this while ++ * loop. 50% of the time we won't take this at all and then of the ++ * cases where we do about 50% of those we only execute once. ++ */ ++ if (!(value & 0xffff)) { ++ nextbit += 0x10; ++ value >>= 16; ++ } ++ ++ if (!(value & 0xff)) { ++ nextbit += 0x08; ++ value >>= 8; ++ } ++ ++ if (!(value & 0xf)) { ++ nextbit += 0x04; ++ value >>= 4; ++ } ++ ++ nextbit += irq_bits[value & 0x7]; ++ if (nextbit > 63) { ++ panic("nextbit out of range: %d\n", nextbit); ++ } ++ return nextbit; ++} ++ ++/* ++ * ubicom32_reserve_handler() ++ * Bogus handler associated with pre-reserved IRQ(s). ++ */ ++static irqreturn_t ubicom32_reserve_handler(int irq, void *dev_id) ++{ ++ BUG(); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * __irq_disable_vector() ++ * Disable the interrupt by clearing the appropriate bit in the ++ * LDSR Mask Register. ++ */ ++static void __irq_disable_vector(unsigned int irq) ++{ ++ ldsr_disable_vector(irq); ++} ++ ++/* ++ * __irq_ack_vector() ++ * Acknowledge the specific interrupt by clearing the associate bit in ++ * hardware ++ */ ++static void __irq_ack_vector(unsigned int irq) ++{ ++ if (irq < 32) { ++ asm volatile ("move.4 INT_CLR0, %0" : : "d" (1 << irq)); ++ } else { ++ asm volatile ("move.4 INT_CLR1, %0" : : "d" (1 << (irq - 32))); ++ } ++} ++ ++/* ++ * __irq_enable_vector() ++ * Clean and then enable the interrupt by setting the appropriate bit in ++ * the LDSR Mask Register. ++ */ ++static void __irq_enable_vector(unsigned int irq) ++{ ++ /* ++ * Acknowledge, really clear the vector. ++ */ ++ __irq_ack_vector(irq); ++ ldsr_enable_vector(irq); ++} ++ ++/* ++ * __irq_mask_vector() ++ */ ++static void __irq_mask_vector(unsigned int irq) ++{ ++ ldsr_mask_vector(irq); ++} ++ ++/* ++ * __irq_unmask_vector() ++ */ ++static void __irq_unmask_vector(unsigned int irq) ++{ ++ ldsr_unmask_vector(irq); ++} ++ ++/* ++ * __irq_end_vector() ++ * Called once an interrupt is completed (reset the LDSR mask). ++ */ ++static void __irq_end_vector(unsigned int irq) ++{ ++ ldsr_unmask_vector(irq); ++} ++ ++#if defined(CONFIG_SMP) ++/* ++ * __irq_set_affinity() ++ * Set the cpu affinity for this interrupt. ++ */ ++static void __irq_set_affinity(unsigned int irq, cpumask_t dest) ++{ ++ smp_set_affinity(irq, dest); ++ irq_desc[irq].affinity = dest; ++} ++#endif ++ ++/* ++ * On-Chip Generic Interrupt function handling. ++ */ ++static struct irq_chip ubicom32_irq_chip = { ++ .name = "Ubicom32", ++ .startup = NULL, ++ .shutdown = NULL, ++ .enable = __irq_enable_vector, ++ .disable = __irq_disable_vector, ++ .ack = __irq_ack_vector, ++ .mask = __irq_mask_vector, ++ .unmask = __irq_unmask_vector, ++ .end = __irq_end_vector, ++#if defined(CONFIG_SMP) ++ .set_affinity = __irq_set_affinity, ++#endif ++}; ++ ++/* ++ * do_IRQ() ++ * Primary interface for handling IRQ() requests. ++ */ ++asmlinkage void do_IRQ(int irq, struct pt_regs *regs) ++{ ++ struct pt_regs *oldregs; ++ struct thread_info *ti; ++ unsigned long sp; ++ ++ IRQ_DECLARE_MEASUREMENT; ++ ++ oldregs = set_irq_regs(regs); ++ ++ /* ++ * Before we call the handler and potentially enable ++ * interrupts, track the nesting level. ++ */ ++ asm volatile ( ++ "and.4 %0, SP, %1 \n\t" ++ : "=d" (sp) ++ : "d" (~(THREAD_SIZE - 1)) ++ : "cc" ++ ); ++ ti = (struct thread_info *)sp; ++ ti->interrupt_nesting++; ++ ++ /* ++ * Check for possible stack overflow on the kernel stack. ++ */ ++ irq_kernel_stack_check(irq, regs); ++ ++ /* ++ * Execute the IRQ handler and any pending SoftIRQ requests. ++ */ ++ BUG_ON(!irqs_disabled()); ++ irq_enter(); ++ ++ BUG_ON(!irqs_disabled()); ++ IRQ_MEASUREMENT_START(); ++ __do_IRQ(irq); ++ IRQ_MEASUREMENT_END(irq); ++ BUG_ON(!irqs_disabled()); ++ ++ IRQ_MEASUREMENT_START(); ++ irq_exit(); ++ IRQ_MEASUREMENT_END(NR_IRQS); ++ BUG_ON(!irqs_disabled()); ++ ++ set_irq_regs(oldregs); ++ ti->interrupt_nesting--; ++} ++ ++/* ++ * irq_soft_alloc() ++ * Allocate a soft IRQ. ++ */ ++int irq_soft_alloc(unsigned int *soft) ++{ ++ if (irq_soft_avail == 0) { ++ printk(KERN_NOTICE "no soft irqs to allocate\n"); ++ return -EFAULT; ++ } ++ ++ *soft = irq_get_lsb(irq_soft_avail); ++ irq_soft_avail &= ~(1 << *soft); ++ return 0; ++} ++ ++/* ++ * ack_bad_irq() ++ * Called to handle an bad irq request. ++ */ ++void ack_bad_irq(unsigned int irq) ++{ ++ printk(KERN_ERR "IRQ: unexpected irq=%d\n", irq); ++ __irq_end_vector(irq); ++} ++ ++/* ++ * show_interrupts() ++ * Return a string that displays the state of each of the interrupts. ++ */ ++int show_interrupts(struct seq_file *p, void *v) ++{ ++ struct irqaction *ap; ++ int irq = *((loff_t *) v); ++ int j; ++ ++ if (irq >= NR_IRQS) { ++ return 0; ++ } ++ ++ if (irq == 0) { ++ seq_puts(p, " "); ++ for_each_online_cpu(j) { ++ seq_printf(p, "CPU%d ", j); ++ } ++ seq_putc(p, '\n'); ++ } ++ ++ ap = irq_desc[irq].action; ++ if (ap) { ++ seq_printf(p, "%3d: ", irq); ++ for_each_online_cpu(j) { ++ seq_printf(p, "%10u ", kstat_irqs_cpu(irq, j)); ++ } ++ seq_printf(p, "%14s ", irq_desc[irq].chip->name); ++ seq_printf(p, "%s", ap->name); ++ for (ap = ap->next; ap; ap = ap->next) { ++ seq_printf(p, ", %s", ap->name); ++ } ++ seq_putc(p, '\n'); ++ } ++ return 0; ++} ++ ++#if defined(CONFIG_DEBUG_IRQMEASURE) ++static unsigned int irq_cycles_to_micro(unsigned int cycles, unsigned int frequency) ++{ ++ unsigned int micro = (cycles / (frequency / 1000000)); ++ return micro; ++} ++ ++/* ++ * irq_measurement_show() ++ * Print out the min, avg, max values for each IRQ ++ * ++ * By request, the max value is reset after each dump. ++ */ ++static int irq_measurement_show(struct seq_file *p, void *v) ++{ ++ struct irqaction *ap; ++ unsigned int freq = processor_frequency(); ++ int irq = *((loff_t *) v); ++ ++ ++ if (irq == 0) { ++ seq_puts(p, "\tmin\tavg\tmax\t(micro-seconds)\n"); ++ } ++ ++ if (irq > NR_IRQS) { ++ return 0; ++ } ++ ++ if (irq == NR_IRQS) { ++ unsigned int min, avg, max; ++ spin_lock(&irq_measurement_lock); ++ min = irq_cycles_to_micro(irq_measurements[irq].min, freq); ++ avg = irq_cycles_to_micro(irq_measurements[irq].avg, freq); ++ max = irq_cycles_to_micro(irq_measurements[irq].max, freq); ++ irq_measurements[irq].max = 0; ++ spin_unlock(&irq_measurement_lock); ++ seq_printf(p, " \t%u\t%u\t%u\tsoftirq\n", min, avg, max); ++ return 0; ++ } ++ ++ ap = irq_desc[irq].action; ++ if (ap) { ++ unsigned int min, avg, max; ++ spin_lock(&irq_measurement_lock); ++ min = irq_cycles_to_micro(irq_measurements[irq].min, freq); ++ avg = irq_cycles_to_micro(irq_measurements[irq].avg, freq); ++ max = irq_cycles_to_micro(irq_measurements[irq].max, freq); ++ irq_measurements[irq].max = 0; ++ spin_unlock(&irq_measurement_lock); ++ seq_printf(p, "%2u:\t%u\t%u\t%u\t%s\n", irq, min, avg, max, ap->name); ++ } ++ return 0; ++} ++ ++static void *irq_measurement_start(struct seq_file *f, loff_t *pos) ++{ ++ return (*pos <= NR_IRQS) ? pos : NULL; ++} ++ ++static void *irq_measurement_next(struct seq_file *f, void *v, loff_t *pos) ++{ ++ (*pos)++; ++ if (*pos > NR_IRQS) ++ return NULL; ++ return pos; ++} ++ ++static void irq_measurement_stop(struct seq_file *f, void *v) ++{ ++ /* Nothing to do */ ++} ++ ++static const struct seq_operations irq_measurement_seq_ops = { ++ .start = irq_measurement_start, ++ .next = irq_measurement_next, ++ .stop = irq_measurement_stop, ++ .show = irq_measurement_show, ++}; ++ ++static int irq_measurement_open(struct inode *inode, struct file *filp) ++{ ++ return seq_open(filp, &irq_measurement_seq_ops); ++} ++ ++static const struct file_operations irq_measurement_fops = { ++ .open = irq_measurement_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++static int __init irq_measurement_init(void) ++{ ++ proc_create("irq_measurements", 0, NULL, &irq_measurement_fops); ++ return 0; ++} ++module_init(irq_measurement_init); ++#endif ++ ++/* ++ * init_IRQ(void) ++ * Initialize the on-chip IRQ subsystem. ++ */ ++void __init init_IRQ(void) ++{ ++ int irq; ++ struct devtree_node *p = NULL; ++ struct devtree_node *iter = NULL; ++ unsigned int mask = 0; ++ unsigned int reserved = 0; ++ ++ /* ++ * Pull out the list of software interrupts that are avialable to ++ * Linux and provide an allocation function for them. The first ++ * 24 interrupts of INT0 are software interrupts. ++ */ ++ irq_soft_avail = 0; ++ if (processor_interrupts(&irq_soft_avail, NULL) < 0) { ++ printk(KERN_WARNING "No Soft IRQ(s) available\n"); ++ } ++ irq_soft_avail &= ((1 << 24) - 1); ++ ++ /* ++ * Initialize all of the on-chip interrupt handling ++ * to use a common set of interrupt functions. ++ */ ++ for (irq = 0; irq < NR_IRQS; irq++) { ++ irq_desc[irq].status = IRQ_DISABLED; ++ irq_desc[irq].action = NULL; ++ irq_desc[irq].depth = 1; ++ set_irq_chip(irq, &ubicom32_irq_chip); ++ } ++ ++ /* ++ * The sendirq of a devnode is not registered within Linux but instead ++ * is used by the software I/O thread. These interrupts are reserved. ++ * The recvirq is used by Linux and registered by a device driver, these ++ * are not reserved. ++ * ++ * recvirq(s) that are in the software interrupt range are not supposed ++ * to be marked as reserved. We track this while we scan the device ++ * nodes. ++ */ ++ p = devtree_find_next(&iter); ++ while (p) { ++ unsigned char sendirq, recvirq; ++ devtree_irq(p, &sendirq, &recvirq); ++ ++ /* ++ * If the sendirq is valid, mark that irq as taken by the ++ * devtree node. ++ */ ++ if (sendirq < NR_IRQS) { ++ ubicom32_reserve_action[sendirq].handler = ++ ubicom32_reserve_handler; ++ ubicom32_reserve_action[sendirq].name = p->name; ++ irq_desc[sendirq].action = ++ &ubicom32_reserve_action[sendirq]; ++ mask |= (1 << sendirq); ++ } ++ ++ /* ++ * Track the relevant recieve IRQ(s) ++ */ ++ if (recvirq < 24) { ++ mask |= (1 << recvirq); ++ } ++ ++ /* ++ * Move to the next node. ++ */ ++ p = devtree_find_next(&iter); ++ } ++ ++ /* ++ * Remove these bits from the irq_soft_avail list and then use the ++ * result as the list of pre-reserved IRQ(s). ++ */ ++ reserved = ~irq_soft_avail & ~mask; ++ for (irq = 0; irq < 24; irq++) { ++ if ((reserved & (1 << irq))) { ++ ubicom32_reserve_action[irq].handler = ++ ubicom32_reserve_handler; ++ ubicom32_reserve_action[irq].name = "reserved"; ++ irq_desc[irq].action = &ubicom32_reserve_action[irq]; ++ } ++ } ++ ++ /* ++ * Initialize the LDSR which is the Ubicom32 programmable ++ * interrupt controller. ++ */ ++ ldsr_init(); ++ ++ /* ++ * The Ubicom trap code needs a 2nd init after IRQ(s) are setup. ++ */ ++ trap_init_interrupt(); ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/ldsr.c +@@ -0,0 +1,1176 @@ ++/* ++ * arch/ubicom32/kernel/ldsr.c ++ * Ubicom32 architecture Linux Device Services Driver Interface ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * ++ * NOTES: ++ * ++ * The LDSR is a programmable interrupt controller that is written in software. ++ * It emulates the behavior of an pic by fielding the interrupts, choosing a ++ * victim thread to take the interrupt and forcing that thread to take a context ++ * switch to the appropriate interrupt handler. ++ * ++ * Because traps are treated as just a special class of interrupts, the LDSR ++ * also handles the processing of traps. ++ * ++ * Because we compile Linux both UP and SMP, we need the LDSR to use ++ * architectural locking that is not "compiled out" when compiling UP. For now, ++ * we use the single atomic bit lock. ++ */ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/profile.h> ++#include <linux/clocksource.h> ++#include <linux/types.h> ++#include <linux/module.h> ++#include <linux/cpumask.h> ++#include <linux/bug.h> ++#include <linux/delay.h> ++#include <asm/ip5000.h> ++#include <asm/atomic.h> ++#include <asm/machdep.h> ++#include <asm/asm-offsets.h> ++#include <asm/traps.h> ++#include <asm/thread.h> ++#include <asm/range-protect.h> ++ ++/* ++ * One can not print from the LDSR so the best we can do is ++ * check a condition and stall all of the threads. ++ */ ++ ++// #define DEBUG_LDSR 1 ++#if defined(DEBUG_LDSR) ++#define DEBUG_ASSERT(cond) \ ++ if (!(cond)) { \ ++ THREAD_STALL; \ ++ } ++#else ++#define DEBUG_ASSERT(cond) ++#endif ++ ++/* ++ * Make global so that we can use it in the RFI code in assembly. ++ */ ++unsigned int ldsr_soft_irq_mask; ++EXPORT_SYMBOL(ldsr_soft_irq_mask); ++ ++static unsigned int ldsr_suspend_mask; ++static unsigned int ldsr_soft_irq; ++static unsigned int ldsr_stack_space[1024]; ++ ++static struct ldsr_register_bank { ++ volatile unsigned int enabled0; ++ volatile unsigned int enabled1; ++ volatile unsigned int mask0; ++ volatile unsigned int mask1; ++ unsigned int total; ++ unsigned int retry; ++ unsigned int backout; ++} ldsr_interrupt; ++ ++/* ++ * Which thread/cpu are we? ++ */ ++static int ldsr_tid = -1; ++ ++#if defined(CONFIG_IRQSTACKS) ++/* ++ * per-CPU IRQ stacks (thread information and stack) ++ * ++ * NOTE: Do not use DEFINE_PER_CPU() as it makes it harder ++ * to find the location of ctx from assembly language. ++ */ ++union irq_ctx { ++ struct thread_info tinfo; ++ u32 stack[THREAD_SIZE/sizeof(u32)]; ++}; ++static union irq_ctx *percpu_irq_ctxs[NR_CPUS]; ++ ++/* ++ * Storage for the interrupt stack. ++ */ ++#if !defined(CONFIG_IRQSTACKS_USEOCM) ++static char percpu_irq_stacks[(NR_CPUS * THREAD_SIZE) + (THREAD_SIZE - 1)]; ++#else ++/* ++ * For OCM, the linker will ensure that space is allocated for the stack ++ * see (vmlinux.lds.S) ++ */ ++static char percpu_irq_stacks[]; ++#endif ++ ++#endif ++ ++/* ++ * Save trap IRQ because we need to un-suspend if it gets set. ++ */ ++static unsigned int ldsr_trap_irq_mask; ++static unsigned int ldsr_trap_irq; ++ ++/* ++ * ret_from_interrupt_to_kernel ++ * Just restore the context and do nothing else. ++ */ ++asmlinkage void ret_from_interrupt_to_kernel(void)__attribute__((naked)); ++ ++/* ++ * ret_from_interrupt_to_user ++ * Call scheduler if needed. Just restore the context. ++ */ ++asmlinkage void ret_from_interrupt_to_user(void)__attribute__((naked)); ++ ++#ifdef DEBUG_LDSR ++u32_t old_sp, old_pc, old_a0, old_a5, old_a3; ++struct pt_regs copy_regs, *copy_save_area; ++#endif ++ ++int __user_mode(unsigned long sp) ++{ ++ ++ u32_t saved_stack_base = sp & ~(ASM_THREAD_SIZE - 1); ++#if defined(CONFIG_IRQSTACKS_USEOCM) ++ if ((union irq_ctx *)saved_stack_base == percpu_irq_ctxs[smp_processor_id()]) { ++ /* ++ * On the interrupt stack. ++ */ ++ return 0; ++ } ++#endif ++ ++ if (!(u32_t)current) { ++ return 0; ++ } ++ return saved_stack_base != ((u32_t)current->stack); ++} ++ ++/* ++ * ldsr_lock_release() ++ * Release the LDSR lock. ++ */ ++static void ldsr_lock_release(void) ++{ ++ UBICOM32_UNLOCK(LDSR_LOCK_BIT); ++} ++ ++/* ++ * ldsr_lock_acquire() ++ * Acquire the LDSR lock, spin if not available. ++ */ ++static void ldsr_lock_acquire(void) ++{ ++ UBICOM32_LOCK(LDSR_LOCK_BIT); ++} ++ ++/* ++ * ldsr_thread_irq_disable() ++ * Disable interrupts for the specified thread. ++ */ ++static void ldsr_thread_irq_disable(unsigned int tid) ++{ ++ unsigned int mask = (1 << tid); ++ ++ asm volatile ( ++ " or.4 scratchpad1, scratchpad1, %0 \n\t" ++ : ++ : "d"(mask) ++ : "cc" ++ ); ++} ++ ++/* ++ * ldsr_thread_get_interrupts() ++ * Get the interrupt state for all threads. ++ */ ++static unsigned long ldsr_thread_get_interrupts(void) ++{ ++ unsigned long ret = 0; ++ asm volatile ( ++ " move.4 %0, scratchpad1 \n\t" ++ : "=r" (ret) ++ : ++ ); ++ return ret; ++} ++ ++/* ++ * ldsr_emulate_and_run() ++ * Emulate the instruction and then set the thread to run. ++ */ ++static void ldsr_emulate_and_run(unsigned int tid) ++{ ++ unsigned int thread_mask = (1 << tid); ++ u32_t write_csr = (tid << 15) | (1 << 14); ++ ++ /* ++ * Emulate the unaligned access. ++ */ ++ unaligned_emulate(tid); ++ ++ /* ++ * Get the thread back in a running state. ++ */ ++ asm volatile ( ++ " setcsr %0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 trap_cause, #0 \n\t" /* Clear the trap cause ++ * register */ ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 mt_dbg_active_set, %1 \n\t" /* Activate thread even if ++ * in dbg/fault state */ ++ " move.4 mt_active_set, %1 \n\t" /* Restart target ++ * thread. */ ++ : ++ : "r" (write_csr), "d" (thread_mask) ++ : "cc" ++ ); ++ thread_enable_mask(thread_mask); ++} ++ ++/* ++ * ldsr_preemptive_context_save() ++ * save thread context from another hardware thread. The other thread must ++ * be stalled. ++ */ ++static inline void ldsr_preemptive_context_save(u32_t thread, ++ struct pt_regs *regs) ++{ ++ /* ++ * Save the current state of the specified thread ++ */ ++ asm volatile ( ++ " move.4 a3, %0 \n\t" ++ ++ /* set src1 from the target thread */ ++ " move.4 csr, %1 \n\t" ++ " setcsr_flush 0 \n\t" ++ " setcsr_flush 0 \n\t" ++ ++ /* copy state from the other thread */ ++ " move.4 "D(PT_D0)"(a3), d0 \n\t" ++ " move.4 "D(PT_D1)"(a3), d1 \n\t" ++ " move.4 "D(PT_D2)"(a3), d2 \n\t" ++ " move.4 "D(PT_D3)"(a3), d3 \n\t" ++ " move.4 "D(PT_D4)"(a3), d4 \n\t" ++ " move.4 "D(PT_D5)"(a3), d5 \n\t" ++ " move.4 "D(PT_D6)"(a3), d6 \n\t" ++ " move.4 "D(PT_D7)"(a3), d7 \n\t" ++ " move.4 "D(PT_D8)"(a3), d8 \n\t" ++ " move.4 "D(PT_D9)"(a3), d9 \n\t" ++ " move.4 "D(PT_D10)"(a3), d10 \n\t" ++ " move.4 "D(PT_D11)"(a3), d11 \n\t" ++ " move.4 "D(PT_D12)"(a3), d12 \n\t" ++ " move.4 "D(PT_D13)"(a3), d13 \n\t" ++ " move.4 "D(PT_D14)"(a3), d14 \n\t" ++ " move.4 "D(PT_D15)"(a3), d15 \n\t" ++ " move.4 "D(PT_A0)"(a3), a0 \n\t" ++ " move.4 "D(PT_A1)"(a3), a1 \n\t" ++ " move.4 "D(PT_A2)"(a3), a2 \n\t" ++ " move.4 "D(PT_A3)"(a3), a3 \n\t" ++ " move.4 "D(PT_A4)"(a3), a4 \n\t" ++ " move.4 "D(PT_A5)"(a3), a5 \n\t" ++ " move.4 "D(PT_A6)"(a3), a6 \n\t" ++ " move.4 "D(PT_SP)"(a3), a7 \n\t" ++ " move.4 "D(PT_ACC0HI)"(a3), acc0_hi \n\t" ++ " move.4 "D(PT_ACC0LO)"(a3), acc0_lo \n\t" ++ " move.4 "D(PT_MAC_RC16)"(a3), mac_rc16 \n\t" ++ " move.4 "D(PT_ACC1HI)"(a3), acc1_hi \n\t" ++ " move.4 "D(PT_ACC1LO)"(a3), acc1_lo \n\t" ++ " move.4 "D(PT_SOURCE3)"(a3), source3 \n\t" ++ " move.4 "D(PT_INST_CNT)"(a3), inst_cnt \n\t" ++ " move.4 "D(PT_CSR)"(a3), csr \n\t" ++ " move.4 "D(PT_DUMMY_UNUSED)"(a3), #0 \n\t" ++ " move.4 "D(PT_INT_MASK0)"(a3), int_mask0 \n\t" ++ " move.4 "D(PT_INT_MASK1)"(a3), int_mask1 \n\t" ++ " move.4 "D(PT_TRAP_CAUSE)"(a3), trap_cause \n\t" ++ " move.4 "D(PT_PC)"(a3), pc \n\t" ++ " move.4 "D(PT_PREVIOUS_PC)"(a3), previous_pc \n\t" ++ /* disable csr thread select */ ++ " movei csr, #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ : ++ : "r" (regs->dn), "d" ((thread << 9) | (1 << 8)) ++ : "a3" ++ ); ++} ++ ++/* ++ * ldsr_rotate_threads() ++ * Simple round robin algorithm for choosing the next cpu ++ */ ++static int ldsr_rotate_threads(unsigned long cpus) ++{ ++ static unsigned char ldsr_bits[8] = { ++ 3, 0, 1, 0, 2, 0, 1, 0 ++ }; ++ ++ static int nextbit; ++ int thisbit; ++ ++ /* ++ * Move the interrupts down so that we consider interrupts from where ++ * we left off, then take the interrupts we would lose and move them ++ * to the top half of the interrupts value. ++ */ ++ cpus = (cpus >> nextbit) | (cpus << ((sizeof(cpus) * 8) - nextbit)); ++ ++ /* ++ * 50% of the time we won't take this at all and then of the cases where ++ * we do about 50% of those we only execute once. ++ */ ++ if (!(cpus & 0xffff)) { ++ nextbit += 16; ++ cpus >>= 16; ++ } ++ ++ if (!(cpus & 0xff)) { ++ nextbit += 8; ++ cpus >>= 8; ++ } ++ ++ if (!(cpus & 0xf)) { ++ nextbit += 4; ++ cpus >>= 4; ++ } ++ ++ nextbit += ldsr_bits[cpus & 0x7]; ++ thisbit = (nextbit & ((sizeof(cpus) * 8) - 1)); ++ nextbit = (thisbit + 1) & ((sizeof(cpus) * 8) - 1); ++ DEBUG_ASSERT(thisbit < THREAD_ARCHITECTURAL_MAX); ++ return thisbit; ++} ++ ++/* ++ * ldsr_rotate_interrupts() ++ * Get rotating next set bit value. ++ */ ++static int ldsr_rotate_interrupts(unsigned long long interrupts) ++{ ++ static unsigned char ldsr_bits[8] = { ++ 3, 0, 1, 0, 2, 0, 1, 0 ++ }; ++ ++ static int nextbit; ++ int thisbit; ++ ++ /* ++ * Move the interrupts down so that we consider interrupts from where ++ * we left off, then take the interrupts we would lose and move them ++ * to the top half of the interrupts value. ++ */ ++ interrupts = (interrupts >> nextbit) | ++ (interrupts << ((sizeof(interrupts) * 8) - nextbit)); ++ ++ /* ++ * 50% of the time we won't take this at all and then of the cases where ++ * we do about 50% of those we only execute once. ++ */ ++ if (!(interrupts & 0xffffffff)) { ++ nextbit += 32; ++ interrupts >>= 32; ++ } ++ ++ if (!(interrupts & 0xffff)) { ++ nextbit += 16; ++ interrupts >>= 16; ++ } ++ ++ if (!(interrupts & 0xff)) { ++ nextbit += 8; ++ interrupts >>= 8; ++ } ++ ++ if (!(interrupts & 0xf)) { ++ nextbit += 4; ++ interrupts >>= 4; ++ } ++ ++ nextbit += ldsr_bits[interrupts & 0x7]; ++ thisbit = (nextbit & ((sizeof(interrupts) * 8) - 1)); ++ nextbit = (thisbit + 1) & ((sizeof(interrupts) * 8) - 1); ++ ++ DEBUG_ASSERT(thisbit < (sizeof(interrupts) * 8)); ++ return thisbit; ++} ++ ++/* ++ * ldsr_backout_or_irq() ++ * ++ * One way or the other this interrupt is not being ++ * processed, make sure that it is reset. We are ++ * not going to call irq_end_vector() so unmask the ++ * interrupt. ++ */ ++static void ldsr_backout_of_irq(int vector, unsigned long tid_mask) ++{ ++#if defined(CONFIG_SMP) ++ if (unlikely(vector == smp_ipi_irq)) { ++ smp_reset_ipi(tid_mask); ++ } ++#endif ++ ldsr_unmask_vector(vector); ++ ldsr_interrupt.backout++; ++} ++ ++#if defined(CONFIG_IRQSTACKS) ++/* ++ * ldsr_choose_savearea_and_returnvec() ++ * Test our current state (user, kernel, interrupt) and set things up. ++ * ++ * This version of the function uses 3 stacks and nests interrupts ++ * on the interrupt stack. ++ */ ++static struct pt_regs *ldsr_choose_savearea_and_returnvec(thread_t tid, u32_t linux_sp, u32_t *pvec) ++{ ++ struct pt_regs *save_area; ++ u32_t masked_linux_sp = linux_sp & ~(THREAD_SIZE - 1); ++ struct thread_info * ti= (struct thread_info *)sw_ksp[tid]; ++ ++#if defined(CONFIG_SMP) ++ union irq_ctx *icp = percpu_irq_ctxs[tid]; ++#else ++ union irq_ctx *icp = percpu_irq_ctxs[0]; ++#endif ++ ++ if (masked_linux_sp == (u32_t)icp) { ++ /* ++ * Fault/Interrupt occurred while on the interrupt stack. ++ */ ++ save_area = (struct pt_regs *)((char *)linux_sp - sizeof(struct pt_regs) - 8); ++ *pvec = (u32_t)(&ret_from_interrupt_to_kernel); ++ } else { ++ /* ++ * Fault/Interrupt occurred while on user/kernel stack. This is a new ++ * first use of the interrupt stack. ++ */ ++ save_area = (struct pt_regs *) ((char *)icp + sizeof(icp->stack) - sizeof(struct pt_regs) - 8); ++ if (masked_linux_sp == (u32_t)ti) { ++ *pvec = (u32_t)(&ret_from_interrupt_to_kernel); ++ } else { ++ *pvec = (u32_t)(&ret_from_interrupt_to_user); ++ } ++ ++ /* ++ * Because the softirq code will execute on the "interrupt" stack, we ++ * need to maintain the knowledge of what "task" was executing on the ++ * cpu. This is done by copying the thread_info->task from the cpu ++ * we are about to context switch into the interrupt contexts thread_info ++ * structure. ++ */ ++ icp->tinfo.task = ti->task; ++ icp->tinfo.preempt_count = ++ (icp->tinfo.preempt_count & ~SOFTIRQ_MASK) | ++ (ti->preempt_count & SOFTIRQ_MASK); ++ icp->tinfo.interrupt_nesting = 0; ++ } ++ save_area->nesting_level = icp->tinfo.interrupt_nesting; ++ return save_area; ++} ++ ++#else ++/* ++ * ldsr_choose_savearea_and_returnvec() ++ * Test our current state (user, kernel, interrupt) and set things up. ++ * ++ * The version of the function uses just the user & kernel stack and ++ * nests interrupts on the existing kernel stack. ++ */ ++static struct pt_regs *ldsr_choose_savearea_and_returnvec(thread_t tid, u32_t linux_sp, u32_t *pvec) ++{ ++ struct pt_regs *save_area; ++ u32_t masked_linux_sp = linux_sp & ~(THREAD_SIZE - 1); ++ struct thread_info *ti = (struct thread_info *)sw_ksp[tid]; ++ ++ if (masked_linux_sp == (u32_t)ti) { ++ /* ++ * Fault/Interrupt occurred while on the kernel stack. ++ */ ++ save_area = (struct pt_regs *)((char *)linux_sp - sizeof(struct pt_regs) - 8); ++ *pvec = (u32_t) (&ret_from_interrupt_to_kernel); ++ } else { ++ /* ++ * Fault/Interrupt occurred while on user stack. ++ */ ++ ti->interrupt_nesting = 0; ++ save_area = (struct pt_regs *)((u32_t)ti + THREAD_SIZE - sizeof(struct pt_regs) - 8); ++ *pvec = (u32_t) (&ret_from_interrupt_to_user); ++ } ++ save_area->nesting_level = ti->interrupt_nesting; ++ return save_area; ++} ++#endif ++ ++/* ++ * ldsr_ctxsw_thread() ++ * Context switch a mainline thread to execute do_IRQ() for the specified ++ * vector. ++ */ ++static void ldsr_ctxsw_thread(int vector, thread_t tid) ++{ ++ u32_t linux_sp; ++ u32_t return_vector; ++ struct pt_regs *save_area, *regs; ++ u32_t thread_mask = (1 << tid); ++ u32_t read_csr = ((tid << 9) | (1 << 8)); ++ u32_t write_csr = (tid << 15) | (1 << 14); ++ u32_t interrupt_vector = (u32_t)(&do_IRQ); ++ ++ unsigned int frame_type = UBICOM32_FRAME_TYPE_INTERRUPT; ++ ++ ++ DEBUG_ASSERT(!thread_is_enabled(tid)); ++ ++ /* ++ * Acquire the necessary global and per thread locks for tid. ++ * As a side effect, we ensure that the thread has not trapped ++ * and return true if it has. ++ */ ++ if (unlikely(thread_is_trapped(tid))) { ++ /* ++ * Read the trap cause, the sp and clear the MT_TRAP bits. ++ */ ++ unsigned int cause; ++ asm volatile ( ++ " setcsr %3 \n\t" ++ " setcsr_flush 0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 %0, TRAP_CAUSE \n\t" ++ " move.4 %1, SP \n\t" ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 MT_BREAK_CLR, %2\n\t" ++ " move.4 MT_TRAP_CLR, %2 \n\t" ++ : "=&r" (cause), "=&r" (linux_sp) ++ : "r" (thread_mask), "m" (read_csr) ++ ); ++ ++ ldsr_backout_of_irq(vector, (1 << tid)); ++ ++#if !defined(CONFIG_UNALIGNED_ACCESS_DISABLED) ++ /* ++ * See if the unaligned trap handler can deal with this. ++ * If so, emulate the instruction and then just restart ++ * the thread. ++ */ ++ if (unaligned_only(cause)) { ++#if defined(CONFIG_UNALIGNED_ACCESS_USERSPACE_ONLY) ++ /* ++ * Check if this is a kernel stack if so we will not ++ * handle the trap ++ */ ++ u32_t masked_linux_sp = linux_sp & ~(THREAD_SIZE - 1); ++ if ((masked_linux_sp != (u32_t)sw_ksp[tid]) && ++ unaligned_only(cause)) { ++ ldsr_emulate_and_run(tid); ++ return; ++ } ++#else ++ ldsr_emulate_and_run(tid); ++ return; ++#endif ++ ++ } ++#endif ++ ++ interrupt_vector = (u32_t)(&trap_handler); ++ frame_type = UBICOM32_FRAME_TYPE_TRAP; ++ } else { ++ /* ++ * Read the target thread's SP ++ */ ++ asm volatile ( ++ " setcsr %1 \n\t" ++ " setcsr_flush 0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 %0, SP \n\t" ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ : "=m" (linux_sp) ++ : "m" (read_csr) ++ ); ++ } ++ ++ /* ++ * We are delivering an interrupt, count it. ++ */ ++ ldsr_interrupt.total++; ++ ++ /* ++ * At this point, we will definitely force this thread to ++ * a new context, show its interrupts as disabled. ++ */ ++ ldsr_thread_irq_disable(tid); ++ ++ /* ++ * Test our current state (user, kernel, interrupt). Save the ++ * appropriate data and setup for the return. ++ */ ++ save_area = ldsr_choose_savearea_and_returnvec(tid, linux_sp, &return_vector); ++ ++ /* ++ * The pt_regs (save_area) contains the type of thread that we are dealing ++ * with (KERNEL/NORMAL) and is copied into each pt_regs area. We get this ++ * from the current tasks kernel pt_regs area that always exists at the ++ * top of the kernel stack. ++ */ ++ regs = (struct pt_regs *)((u32_t)sw_ksp[tid] + THREAD_SIZE - sizeof(struct pt_regs) - 8); ++ save_area->thread_type = regs->thread_type; ++ ++ /* ++ * Preserve the context of the Linux thread. ++ */ ++ ldsr_preemptive_context_save(tid, save_area); ++ ++ /* ++ * Load the fram_type into the save_area. ++ */ ++ save_area->frame_type = frame_type; ++ ++#ifdef CONFIG_STOP_ON_TRAP ++ /* ++ * Before we get backtrace and showing stacks working well, it sometimes ++ * helps to enter the debugger when a trap occurs before we change the ++ * thread to handle the fault. This optional code causes all threads to ++ * stop on every trap frame. One assumes that GDB connected via the ++ * mailbox interface will be used to recover from this state. ++ */ ++ if (frame_type == UBICOM32_FRAME_TYPE_TRAP) { ++ THREAD_STALL; ++ } ++#endif ++ ++#ifdef DEBUG_LDSR ++ copy_regs = *save_area; ++ copy_save_area = save_area; ++ ++ old_a0 = save_area->an[0]; ++ old_a3 = save_area->an[3]; ++ old_sp = save_area->an[7]; ++ old_a5 = save_area->an[5]; ++ old_pc = save_area->pc; ++#endif ++ ++ /* ++ * Now we have to switch the kernel thread to run do_IRQ function. ++ * Set pc to do_IRQ ++ * Set d0 to vector ++ * Set d1 to save_area. ++ * Set a5 to the proper return vector. ++ */ ++ asm volatile ( ++ " setcsr %0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 d0, %5 \n\t" /* d0 = 0 vector # */ ++ " move.4 d1, %1 \n\t" /* d1 = save_area */ ++ " move.4 sp, %1 \n\t" /* sp = save_area */ ++ " move.4 a5, %2 \n\t" /* a5 = return_vector */ ++ " move.4 pc, %3 \n\t" /* pc = do_IRQ routine. */ ++ " move.4 trap_cause, #0 \n\t" /* Clear the trap cause ++ * register */ ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " enable_kernel_ranges %4 \n\t" ++ " move.4 mt_dbg_active_set, %4 \n\t" /* Activate thread even if ++ * in dbg/fault state */ ++ " move.4 mt_active_set, %4 \n\t" /* Restart target ++ * thread. */ ++ : ++ : "r" (write_csr), "r" (save_area), ++ "r" (return_vector), "r" (interrupt_vector), ++ "d" (thread_mask), "r" (vector) ++ : "cc" ++ ); ++ thread_enable_mask(thread_mask); ++} ++ ++/* ++ * ldsr_deliver_interrupt() ++ * Deliver the interrupt to one of the threads or all of the threads. ++ */ ++static void ldsr_deliver_interrupt(int vector, ++ unsigned long deliver_to, ++ int all) ++{ ++ unsigned long disabled_threads; ++ unsigned long possible_threads; ++ unsigned long trapped_threads; ++ unsigned long global_locks; ++ ++ /* ++ * Disable all of the threads that we might want to send ++ * this interrupt to. ++ */ ++retry: ++ DEBUG_ASSERT(deliver_to); ++ thread_disable_mask(deliver_to); ++ ++ /* ++ * If any threads are in the trap state, we have to service the ++ * trap for those threads first. ++ */ ++ asm volatile ( ++ "move.4 %0, MT_TRAP \n\t" ++ : "=r" (trapped_threads) ++ : ++ ); ++ ++ trapped_threads &= deliver_to; ++ if (unlikely(trapped_threads)) { ++ /* ++ * all traps will be handled, so clear the trap bit before restarting any threads ++ */ ++ ubicom32_clear_interrupt(ldsr_trap_irq); ++ ++ /* ++ * Let the remaining untrapped threads, continue. ++ */ ++ deliver_to &= ~trapped_threads; ++ if (deliver_to) { ++ thread_enable_mask(deliver_to); ++ } ++ ++ /* ++ * For the trapped threads force them to handle ++ * a trap. ++ */ ++ while (trapped_threads) { ++ unsigned long which = ffz(~trapped_threads); ++ trapped_threads &= ~(1 << which); ++ ldsr_ctxsw_thread(vector, which); ++ } ++ return; ++ } ++ ++ /* ++ * Can we deliver an interrupt to any of the threads? ++ */ ++ disabled_threads = ldsr_thread_get_interrupts(); ++ possible_threads = deliver_to & ~disabled_threads; ++ if (unlikely(!possible_threads)) { ++#if defined(CONFIG_SMP) ++ /* ++ * In the SMP case, we can not wait because 1 cpu might be ++ * sending an IPI to another cpu which is currently blocked. ++ * The only way to ensure IPI delivery is to backout and ++ * keep trying. For SMP, we don't sleep until the interrupts ++ * are delivered. ++ */ ++ thread_enable_mask(deliver_to); ++ ldsr_backout_of_irq(vector, deliver_to); ++ return; ++#else ++ /* ++ * In the UP case, we have nothing to do so we should wait. ++ * ++ * Since the INT_MASK0 and INT_MASK1 are "re-loaded" before we ++ * suspend in the outer loop, we do not need to save them here. ++ * ++ * We test that we were awakened for our specific interrupts ++ * because the ldsr mask/unmask operations will force the ldsr ++ * awake even if the interrupt on the mainline thread is not ++ * completed. ++ */ ++ unsigned int scratch = 0; ++ thread_enable_mask(deliver_to); ++ asm volatile ( ++ " move.4 INT_MASK0, %1 \n\t" ++ " move.4 INT_MASK1, #0 \n\t" ++ ++ "1: suspend \n\t" ++ " move.4 %0, INT_STAT0 \n\t" ++ " and.4 %0, %0, %1 \n\t" ++ " jmpeq.f 1b \n\t" ++ ++ " move.4 INT_CLR0, %2 \n\t" ++ : "+r" (scratch) ++ : "d" (ldsr_suspend_mask), "r" (ldsr_soft_irq_mask) ++ : "cc" ++ ); ++ ++ /* ++ * This delay is sized to coincide with the time it takes a ++ * thread to complete the exit (see return_from_interrupt). ++ */ ++ ldsr_interrupt.retry++; ++ __delay(10); ++ goto retry; ++#endif ++ } ++ ++ /* ++ * If any of the global locks are held, we can not deliver any ++ * interrupts, we spin delay(10) and then try again. If our ++ * spinning becomes a bottle neck, we will need to suspend but for ++ * now lets just spin. ++ */ ++ asm volatile ( ++ "move.4 %0, scratchpad1 \n\t" ++ : "=r" (global_locks) ++ : ++ ); ++ if (unlikely(global_locks & 0xffff0000)) { ++ thread_enable_mask(deliver_to); ++ ++ /* ++ * This delay is sized to coincide with the average time it ++ * takes a thread to release a global lock. ++ */ ++ ldsr_interrupt.retry++; ++ __delay(10); ++ goto retry; ++ } ++ ++ /* ++ * Deliver to one cpu. ++ */ ++ if (!all) { ++ /* ++ * Find our victim and then enable everyone else. ++ */ ++ unsigned long victim = ldsr_rotate_threads(possible_threads); ++ DEBUG_ASSERT((deliver_to & (1 << victim))); ++ DEBUG_ASSERT((possible_threads & (1 << victim))); ++ ++ deliver_to &= ~(1 << victim); ++ if (deliver_to) { ++ thread_enable_mask(deliver_to); ++ } ++ ldsr_ctxsw_thread(vector, victim); ++ return; ++ } ++ ++ /* ++ * If we can't deliver to some threads, wake them ++ * back up and reset things to deliver to them. ++ */ ++ deliver_to &= ~possible_threads; ++ if (unlikely(deliver_to)) { ++ thread_enable_mask(deliver_to); ++ ldsr_backout_of_irq(vector, deliver_to); ++ } ++ ++ /* ++ * Deliver to all possible threads(s). ++ */ ++ while (possible_threads) { ++ unsigned long victim = ffz(~possible_threads); ++ possible_threads &= ~(1 << victim); ++ ldsr_ctxsw_thread(vector, victim); ++ } ++} ++ ++/* ++ * ldsr_thread() ++ * This thread acts as the interrupt controller for Linux. ++ */ ++static void ldsr_thread(void *arg) ++{ ++ int stat0; ++ int stat1; ++ int interrupt0; ++ int interrupt1; ++ long long interrupts; ++ unsigned long cpus; ++ ++#if !defined(CONFIG_SMP) ++ /* ++ * In a non-smp configuration, we can not use the cpu(s) arrays because ++ * there is not a 1-1 correspondence between cpus(s) and our threads. ++ * Thus we must get a local idea of the mainline threads and use the ++ * one and only 1 set as the victim. We do this once before the ldsr ++ * loop. ++ * ++ * In the SMP case, we will use the cpu(s) map to determine which cpu(s) ++ * are valid to send interrupts to. ++ */ ++ int victim = 0; ++ unsigned int mainline = thread_get_mainline(); ++ if (mainline == 0) { ++ panic("no mainline Linux threads to interrupt"); ++ return; ++ } ++ victim = ffz(~mainline); ++ cpus = (1 << victim); ++#endif ++ ++ while (1) { ++ /* ++ * If one changes this code not to reload the INT_MASK(s), you ++ * need to know that code in the lock waiting above does not ++ * reset the MASK registers back; so that code will need to be ++ * changed. ++ */ ++ ldsr_lock_acquire(); ++ asm volatile ( ++ " move.4 INT_MASK0, %0 \n\t" ++ " move.4 INT_MASK1, %1 \n\t" ++ : ++ : "U4" (ldsr_interrupt.mask0), "U4" (ldsr_interrupt.mask1) ++ ); ++ ldsr_lock_release(); ++ thread_suspend(); ++ ++ /* ++ * Read the interrupt status registers ++ */ ++ asm volatile ( ++ "move.4 %0, INT_STAT0 \n\t" ++ "move.4 %1, INT_STAT1 \n\t" ++ : "=r" (stat0), "=r" (stat1) ++ : ++ ); ++ ++ /* ++ * We only care about interrupts that we have been told to care ++ * about. The interrupt must be enabled, unmasked, and have ++ * occurred in the hardware. ++ */ ++ ldsr_lock_acquire(); ++ interrupt0 = ldsr_interrupt.enabled0 & ++ ldsr_interrupt.mask0 & stat0; ++ interrupt1 = ldsr_interrupt.enabled1 & ++ ldsr_interrupt.mask1 & stat1; ++ ldsr_lock_release(); ++ ++ /* ++ * For each interrupt in the "snapshot" we will mask the ++ * interrupt handle the interrupt (typically calling do_IRQ()). ++ * ++ * The interrupt is unmasked by desc->chip->end() function in ++ * the per chip generic interrupt handling code ++ * (arch/ubicom32/kernel/irq.c).8 ++ */ ++ interrupts = ((unsigned long long)interrupt1 << 32) | ++ interrupt0; ++ while (interrupts) { ++ int all = 0; ++ int vector = ldsr_rotate_interrupts(interrupts); ++ interrupts &= ~((unsigned long long)1 << vector); ++ ++ /* ++ * Now mask off this vector so that the LDSR ignores ++ * it until it is acknowledged. ++ */ ++ ldsr_mask_vector(vector); ++#if !defined(CONFIG_SMP) ++ ldsr_deliver_interrupt(vector, cpus, all); ++#else ++ cpus = smp_get_affinity(vector, &all); ++ if (cpus) { ++ ldsr_deliver_interrupt(vector, cpus, all); ++ } ++#endif ++ } ++ } ++ ++ /* NOTREACHED */ ++} ++ ++/* ++ * ldsr_mask_vector() ++ * Temporarily mask the interrupt vector, turn off the bit in the mask ++ * register. ++ */ ++void ldsr_mask_vector(unsigned int vector) ++{ ++ unsigned int mask; ++ if (vector < 32) { ++ mask = ~(1 << vector); ++ ldsr_lock_acquire(); ++ ldsr_interrupt.mask0 &= mask; ++ ldsr_lock_release(); ++ thread_resume(ldsr_tid); ++ return; ++ } ++ ++ mask = ~(1 << (vector - 32)); ++ ldsr_lock_acquire(); ++ ldsr_interrupt.mask1 &= mask; ++ ldsr_lock_release(); ++ thread_resume(ldsr_tid); ++} ++ ++/* ++ * ldsr_unmask_vector() ++ * Unmask the interrupt vector so that it can be used, turn on the bit in ++ * the mask register. ++ * ++ * Because it is legal for the interrupt path to disable an interrupt, ++ * the unmasking code must ensure that disabled interrupts are not ++ * unmasked. ++ */ ++void ldsr_unmask_vector(unsigned int vector) ++{ ++ unsigned int mask; ++ if (vector < 32) { ++ mask = (1 << vector); ++ ldsr_lock_acquire(); ++ ldsr_interrupt.mask0 |= (mask & ldsr_interrupt.enabled0); ++ ldsr_lock_release(); ++ thread_resume(ldsr_tid); ++ return; ++ } ++ ++ mask = (1 << (vector - 32)); ++ ldsr_lock_acquire(); ++ ldsr_interrupt.mask1 |= (mask & ldsr_interrupt.enabled1); ++ ldsr_lock_release(); ++ thread_resume(ldsr_tid); ++} ++ ++/* ++ * ldsr_enable_vector() ++ * The LDSR implements an interrupt controller and has a local (to the ++ * LDSR) copy of its interrupt mask. ++ */ ++void ldsr_enable_vector(unsigned int vector) ++{ ++ unsigned int mask; ++ if (vector < 32) { ++ mask = (1 << vector); ++ ldsr_lock_acquire(); ++ ldsr_interrupt.enabled0 |= mask; ++ ldsr_interrupt.mask0 |= mask; ++ ldsr_lock_release(); ++ thread_resume(ldsr_tid); ++ return; ++ } ++ ++ mask = (1 << (vector - 32)); ++ ldsr_lock_acquire(); ++ ldsr_interrupt.enabled1 |= mask; ++ ldsr_interrupt.mask1 |= mask; ++ ldsr_lock_release(); ++ thread_resume(ldsr_tid); ++} ++ ++/* ++ * ldsr_disable_vector() ++ * The LDSR implements an interrupt controller and has a local (to the ++ * LDSR) copy of its interrupt mask. ++ */ ++void ldsr_disable_vector(unsigned int vector) ++{ ++ unsigned int mask; ++ ++ if (vector < 32) { ++ mask = ~(1 << vector); ++ ldsr_lock_acquire(); ++ ldsr_interrupt.enabled0 &= mask; ++ ldsr_interrupt.mask0 &= mask; ++ ldsr_lock_release(); ++ thread_resume(ldsr_tid); ++ return; ++ } ++ ++ mask = ~(1 << (vector - 32)); ++ ldsr_lock_acquire(); ++ ldsr_interrupt.enabled1 &= mask; ++ ldsr_interrupt.mask1 &= mask; ++ ldsr_lock_release(); ++ thread_resume(ldsr_tid); ++} ++ ++/* ++ * ldsr_get_threadid() ++ * Return the threadid of the LDSR thread. ++ */ ++thread_t ldsr_get_threadid(void) ++{ ++ return ldsr_tid; ++} ++ ++/* ++ * ldsr_set_trap_irq() ++ * Save away the trap Soft IRQ ++ * ++ * See the per thread lock suspend code above for an explination. ++ */ ++void ldsr_set_trap_irq(unsigned int irq) ++{ ++ ldsr_trap_irq = irq; ++ ldsr_trap_irq_mask = (1 << irq); ++ ldsr_suspend_mask |= ldsr_trap_irq_mask; ++} ++ ++/* ++ * ldsr_init() ++ * Initialize the LDSR (Interrupt Controller) ++ */ ++void ldsr_init(void) ++{ ++#if defined(CONFIG_IRQSTACKS) ++ int i; ++ union irq_ctx *icp; ++#endif ++ ++ void *stack_high = (void *)ldsr_stack_space; ++ stack_high += sizeof(ldsr_stack_space); ++ stack_high -= 8; ++ ++ ++ /* ++ * Obtain a soft IRQ to use ++ */ ++ if (irq_soft_alloc(&ldsr_soft_irq) < 0) { ++ panic("no software IRQ is available\n"); ++ return; ++ } ++ ldsr_soft_irq_mask |= (1 << ldsr_soft_irq); ++ ldsr_suspend_mask |= ldsr_soft_irq_mask; ++ ++ /* ++ * Now allocate and start the LDSR thread. ++ */ ++ ldsr_tid = thread_alloc(); ++ if (ldsr_tid < 0) { ++ panic("no thread available to run LDSR"); ++ return; ++ } ++ ++#if defined(CONFIG_IRQSTACKS) ++ /* ++ * Initialize the per-cpu irq thread_info structure that ++ * is at the top of each per-cpu irq stack. ++ */ ++ icp = (union irq_ctx *) ++ (((unsigned long)percpu_irq_stacks + (THREAD_SIZE - 1)) & ~(THREAD_SIZE - 1)); ++ for (i = 0; i < NR_CPUS; i++) { ++ struct thread_info *ti = &(icp->tinfo); ++ ti->task = NULL; ++ ti->exec_domain = NULL; ++ ti->cpu = i; ++ ti->preempt_count = 0; ++ ti->interrupt_nesting = 0; ++ percpu_irq_ctxs[i] = icp++; ++ } ++#endif ++ thread_start(ldsr_tid, ldsr_thread, NULL, ++ stack_high, THREAD_TYPE_NORMAL); ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/Makefile +@@ -0,0 +1,64 @@ ++# ++# arch/ubicom32/kernel/Makefile ++# Main Makefile for the Ubicom32 arch directory. ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++ ++extra-y := head.o vmlinux.lds ++ ++obj-y += \ ++ devtree.o \ ++ dma.o \ ++ flat.o \ ++ init_task.o \ ++ irq.o \ ++ ldsr.o \ ++ os_node.o \ ++ process.o \ ++ processor.o \ ++ ptrace.o \ ++ setup.o \ ++ signal.o \ ++ stacktrace.o \ ++ sys_ubicom32.o \ ++ syscalltable.o \ ++ thread.o \ ++ time.o \ ++ traps.o \ ++ ubicom32_context_switch.o \ ++ ubicom32_ksyms.o \ ++ ubicom32_syscall.o \ ++ unaligned_trap.o ++ ++obj-$(CONFIG_MODULES) += module.o ++obj-$(CONFIG_COMEMPCI) += comempci.o ++obj-$(CONFIG_SMP) += smp.o topology.o ++obj-$(CONFIG_ACCESS_OK_CHECKS_ENABLED) += uaccess.o ++obj-$(CONFIG_GENERIC_CLOCKEVENTS) += timer_device.o ++obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += timer_broadcast.o ++ ++ifndef CONFIG_GENERIC_CLOCKEVENTS ++obj-y += timer_tick.o ++endif +--- /dev/null ++++ b/arch/ubicom32/kernel/module.c +@@ -0,0 +1,463 @@ ++/* ++ * arch/ubicom32/kernel/module.c ++ * Ubicom32 architecture loadable module support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/moduleloader.h> ++#include <linux/bug.h> ++#include <linux/elf.h> ++#include <linux/vmalloc.h> ++#include <linux/fs.h> ++#include <linux/string.h> ++#include <linux/kernel.h> ++#include <asm/ocm-alloc.h> ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(fmt...) ++#endif ++ ++static void _module_free_ocm(struct module *mod) ++{ ++ printk(KERN_INFO "module arch cleanup %s: OCM instruction memory free " ++ " of %d @%p\n", mod->name, mod->arch.ocm_inst_size, ++ mod->arch.ocm_inst); ++ ++ if (mod->arch.ocm_inst) { ++ ocm_inst_free(mod->arch.ocm_inst); ++ mod->arch.ocm_inst = 0; ++ mod->arch.ocm_inst_size = 0; ++ } ++} ++ ++void *module_alloc(unsigned long size) ++{ ++ if (size == 0) ++ return NULL; ++ return vmalloc(size); ++} ++ ++ ++/* Free memory returned from module_alloc */ ++void module_free(struct module *mod, void *module_region) ++{ ++ vfree(module_region); ++ /* FIXME: If module_region == mod->init_region, trim exception ++ table entries. */ ++ ++ /* ++ * This is expected to be final module free, use this to prune the ++ * ocm ++ */ ++ if (module_region && module_region == mod->module_core) ++ _module_free_ocm(mod); ++ ++} ++ ++/* ++ * module_frob_arch_sections() ++ * Called from kernel/module.c allowing arch specific handling of ++ * sections/headers. ++ */ ++int module_frob_arch_sections(Elf_Ehdr *hdr, ++ Elf_Shdr *sechdrs, ++ char *secstrings, ++ struct module *mod) ++{ ++ Elf_Shdr *s, *sechdrs_end; ++ void *ocm_inst = NULL; ++ int ocm_inst_size = 0; ++ ++ /* ++ * Ubicom32 v3 and v4 are almost binary compatible but not completely. ++ * To be safe check that the module was compiled with the correct -march ++ * which is flags. ++ */ ++#ifdef CONFIG_UBICOM32_V4 ++ if ((hdr->e_flags & 0xFFFF) != EF_UBICOM32_V4) { ++ printk(KERN_WARNING "Module %s was not compiled for " ++ "ubicom32v4, elf_flags:%x,\n", ++ mod->name, hdr->e_flags); ++ return -ENOEXEC; ++ } ++#elif defined CONFIG_UBICOM32_V3 ++ if ((hdr->e_flags & 0xFFFF) != EF_UBICOM32_V3) { ++ printk(KERN_WARNING "Module %s was not compiled for " ++ "ubicom32v3, elf_flags:%x\n", ++ mod->name, hdr->e_flags); ++ return -ENOEXEC; ++ } ++#else ++#error Unknown/Unsupported ubicom32 architecture. ++#endif ++ ++ /* ++ * XXX: sechdrs are vmalloced in kernel/module.c ++ * and would be vfreed just after module is loaded, ++ * so we hack to keep the only information we needed ++ * in mod->arch to correctly free L1 I/D sram later. ++ * NOTE: this breaks the semantic of mod->arch structure. ++ */ ++ sechdrs_end = sechdrs + hdr->e_shnum; ++ for (s = sechdrs; s < sechdrs_end; ++s) { ++ if (strncmp(".ocm_text", secstrings + s->sh_name, 9) == 0) ++ ocm_inst_size += s->sh_size; ++ } ++ ++ if (!ocm_inst_size) ++ return 0; ++ ++ ocm_inst = ocm_inst_alloc(ocm_inst_size, 0 /* internal */); ++ if (ocm_inst == NULL) { ++#ifdef CONFIG_OCM_MODULES_FALLBACK_TO_DDR ++ printk(KERN_WARNING ++ "module %s: OCM instruction memory allocation of %d" ++ "failed, fallback to DDR\n", mod->name, ocm_inst_size); ++ return 0; ++#else ++ printk(KERN_ERR ++ "module %s: OCM instruction memory allocation of %d" ++ "failed.\n", mod->name, ocm_inst_size); ++ return -ENOMEM; ++#endif ++ } ++ ++ mod->arch.ocm_inst = ocm_inst; ++ mod->arch.ocm_inst_size = ocm_inst_size; ++ ++ printk(KERN_INFO ++ "module %s: OCM instruction memory allocation of %d @%p\n", ++ mod->name, mod->arch.ocm_inst_size, mod->arch.ocm_inst); ++ ++ for (s = sechdrs; s < sechdrs_end; ++s) { ++ if (strncmp(".ocm_text", secstrings + s->sh_name, 9) == 0) { ++ memcpy(ocm_inst, (void *)s->sh_addr, s->sh_size); ++ s->sh_flags &= ~SHF_ALLOC; ++ s->sh_addr = (unsigned long)ocm_inst; ++ ocm_inst += s->sh_size; ++ } ++ } ++ ++ return 0; ++} ++ ++int apply_relocate(Elf32_Shdr *sechdrs, ++ const char *strtab, ++ unsigned int symindex, ++ unsigned int relsec, ++ struct module *me) ++{ ++ DEBUGP("Invalid Applying relocate section %u to %u\n", relsec, ++ sechdrs[relsec].sh_info); ++ return -EINVAL; ++} ++ ++int apply_relocate_add(Elf32_Shdr *sechdrs, ++ const char *strtab, ++ unsigned int symindex, ++ unsigned int relsec, ++ struct module *me) ++{ ++ unsigned int i; ++ Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; ++ Elf32_Sym *sym; ++ uint32_t *location; ++ uint32_t insn; ++ ++ DEBUGP("Applying relocate_add section %u to %u\n", relsec, ++ sechdrs[relsec].sh_info); ++ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { ++ uint32_t v; ++ const int elf32_rtype = ELF32_R_TYPE(rel[i].r_info); ++ ++ /* This is where to make the change */ ++ location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr ++ + rel[i].r_offset; ++ /* This is the symbol it is referring to. Note that all ++ undefined symbols have been resolved. */ ++ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr ++ + ELF32_R_SYM(rel[i].r_info); ++ ++ v = rel[i].r_addend + sym->st_value; ++ ++ ++ switch (elf32_rtype) { ++ case R_UBICOM32_32: ++ { ++ /* ++ * Store the 32 bit relocation as is. ++ */ ++ *location = v; ++ break; ++ } ++ case R_UBICOM32_HI24: ++ { ++ /* ++ * 24 bit relocation that is part of the MOVEAI ++ * instruction. The 24 bits come from bits 7 - 30 of the ++ * relocation. Theses bits eventually get split into 2 ++ * fields in the instruction encoding. ++ * ++ * - Bits 7 - 27 of the relocation are encoded into bits ++ * 0 - 20 of the instruction. ++ * ++ * - Bits 28 - 30 of the relocation are encoded into ++ * bit 24 - 26 of the instruction. ++ */ ++ uint32_t valid24 = (v >> 7) & 0xffffff; ++ insn = *location; ++ ++ insn &= ~(0x1fffff | (0x7 << 24)); ++ insn |= (valid24 & 0x1fffff); ++ insn |= ((valid24 & 0xe00000) << 3); ++ *location = insn; ++ } ++ break; ++ case R_UBICOM32_LO7_S: ++ case R_UBICOM32_LO7_2_S: ++ case R_UBICOM32_LO7_4_S: ++ { ++ /* ++ * Bits 0 - 6 of the relocation are encoded into the ++ * 7bit unsigned immediate fields of the SOURCE-1 field ++ * of the instruction. The immediate value is left ++ * shifted by (0, 1, 2) based on the operand size. ++ */ ++ uint32_t valid7 = v & 0x7f; ++ insn = *location; ++ ++ if (elf32_rtype == R_UBICOM32_LO7_2_S) { ++ valid7 >>= 1; ++ } else if (elf32_rtype == R_UBICOM32_LO7_4_S) { ++ valid7 >>= 2; ++ } ++ ++ insn &= ~(0x1f | (0x3 << 8)); ++ insn |= (valid7 & 0x1f); ++ insn |= ((valid7 & 0x60) << 3); ++ *location = insn; ++ } ++ break; ++ case R_UBICOM32_LO7_D: ++ case R_UBICOM32_LO7_2_D: ++ case R_UBICOM32_LO7_4_D: ++ { ++ /* ++ * Bits 0 - 6 of the relocation are encoded into the ++ * 7bit unsigned immediate fields of the DESTINATION ++ * field of the instruction. The immediate value is ++ * left shifted by (0, 1, 2) based on the operand size. ++ */ ++ uint32_t valid7 = v & 0x7f; ++ insn = *location; ++ ++ if (elf32_rtype == R_UBICOM32_LO7_2_D) { ++ valid7 >>= 1; ++ } else if (elf32_rtype == R_UBICOM32_LO7_4_D) { ++ valid7 >>= 2; ++ } ++ ++ insn &= ~((0x1f | (0x3 << 8)) << 16); ++ insn |= ((valid7 & 0x1f) << 16); ++ insn |= ((valid7 & 0x60) << 19); ++ *location = insn; ++ } ++ break; ++ case R_UBICOM32_LO7_CALLI: ++ case R_UBICOM32_LO16_CALLI: ++ { ++ /* ++ * Extract the offset for a CALLI instruction. The ++ * offsets can be either 7 bits or 18 bits. Since all ++ * instructions in ubicom32 architecture are at work ++ * aligned addresses the truncated offset is right ++ * shifted by 2 before being encoded in the instruction. ++ */ ++ uint32_t val; ++ if (elf32_rtype == R_UBICOM32_LO7_CALLI) { ++ val = v & 0x7f; ++ } else { ++ val = v & 0x3ffff; ++ } ++ ++ val >>= 2; ++ ++ insn = *location; ++ ++ insn &= ~0x071f071f; ++ insn |= (val & 0x1f) << 0; ++ val >>= 5; ++ insn |= (val & 0x07) << 8; ++ val >>= 3; ++ insn |= (val & 0x1f) << 16; ++ val >>= 5; ++ insn |= (val & 0x07) << 24; ++ *location = insn; ++ } ++ break; ++ case R_UBICOM32_24_PCREL: ++ { ++ /* ++ * Extract 26 bit signed PC relative offset for CALL ++ * instructions. Since instruction addresses are word ++ * aligned the offset is right shited by 2 before ++ * encoding into instruction. ++ */ ++ int32_t val = v - (int32_t)location; ++ ++ /* ++ * Check that the top 7 bits are all equal to the sign ++ * bit (26), i.e all 0's or all 1's. If they are not then ++ * the absolute difference is greater than 25 bits. ++ */ ++ if (((uint32_t)val & 0xFE000000) != 0xFE000000 && ++ ((uint32_t)val & 0xFE000000) != 0x0) { ++ /* ++ * The relocation is beyond our addressable ++ * range with a 26 bit call. ++ */ ++ printk(KERN_ERR "module %s: PC Relative " ++ "relocation out of range: " ++ "%u (%x->%x, %x)\n", ++ me->name, elf32_rtype, ++ v, (uint32_t) location, val); ++ return -ENOEXEC; ++ } ++ ++ val = (val & 0x3ffffff) >> 2; ++ insn = *location; ++ insn = insn & 0xf8e00000; ++ ++ insn |= (val >> 21) << 24; ++ insn |= (val & 0x1fffff); ++ *location = insn; ++ } ++ break; ++ case R_UBICOM32_LO16: ++ case R_UBICOM32_HI16: ++ { ++ /* ++ * 16 bit immediate value that is encoded into bit 0 - ++ * 15 of the instruction. ++ */ ++ uint32_t val; ++ ++ if (elf32_rtype == R_UBICOM32_LO16) { ++ val = v & 0xffff; ++ } else { ++ val = (v >> 16) & 0xffff; ++ } ++ ++ insn = *location; ++ insn &= 0xffff0000; ++ ++ insn |= val; ++ *location = insn; ++ } ++ break; ++ case R_UBICOM32_21_PCREL: ++ { ++ /* ++ * Extract 23 bit signed PC relative offset for JMP<cc> ++ * instructions. Since instruction addresses are word ++ * aligned the offset is right shited by 2 before ++ * encoding into instruction. ++ */ ++ int32_t val = v - (int32_t)location; ++ ++ val = (val & 0x7fffff) >> 2; ++ insn = *location; ++ insn = insn & 0xffe00000; ++ ++ insn |= (val >> 21) << 24; ++ insn |= val; ++ *location = insn; ++ } ++ break; ++ default: ++ BUG(); ++ printk(KERN_ERR "module %s: Unknown relocation: %u\n", ++ me->name, elf32_rtype); ++ return -ENOEXEC; ++ } ++ } ++ return 0; ++} ++ ++int module_finalize(const Elf_Ehdr *hdr, ++ const Elf_Shdr *sechdrs, ++ struct module *mod) ++{ ++ unsigned int i, strindex = 0, symindex = 0; ++ char *secstrings; ++ int err; ++ ++ err = module_bug_finalize(hdr, sechdrs, mod); ++ if (err) ++ return err; ++ ++ if (!mod->arch.ocm_inst) { ++ /* ++ * No OCM code, so nothing more to do. ++ */ ++ return 0; ++ } ++ ++ secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; ++ ++ for (i = 1; i < hdr->e_shnum; i++) { ++ /* Internal symbols and strings. */ ++ if (sechdrs[i].sh_type == SHT_SYMTAB) { ++ symindex = i; ++ strindex = sechdrs[i].sh_link; ++ } ++ } ++ ++ for (i = 1; i < hdr->e_shnum; i++) { ++ const char *strtab = (char *)sechdrs[strindex].sh_addr; ++ unsigned int info = sechdrs[i].sh_info; ++ ++ /* Not a valid relocation section? */ ++ if (info >= hdr->e_shnum) ++ continue; ++ ++ if ((sechdrs[i].sh_type == SHT_RELA) && ++ (strncmp(".rela.ocm_text", ++ secstrings + sechdrs[i].sh_name, 5 + 9) == 0)) { ++ err = apply_relocate_add((Elf_Shdr *) sechdrs, strtab, ++ symindex, i, mod); ++ if (err) ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++void module_arch_cleanup(struct module *mod) ++{ ++ module_bug_cleanup(mod); ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/os_node.c +@@ -0,0 +1,80 @@ ++/* ++ * arch/ubicom32/kernel/os_node.c ++ * <TODO: Replace with short file description> ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ */ ++#include "linux/types.h" ++#include "linux/linkage.h" ++#include "linux/uts.h" ++#include "linux/utsrelease.h" ++#include "linux/version.h" ++#include <asm/ocm_size.h> ++#include <asm/devtree.h> ++#include <asm/ip5000.h> ++ ++extern asmlinkage void *_start; ++ ++/* ++ * This file provides static information to the boot code allowing it to decide ++ * if the os is compatible. Thus hopefully enabling the boot code to prevent ++ * accidentally booting a kernel that has no hope of running. ++ */ ++struct os_node { ++ struct devtree_node node; ++ unsigned long version; /* Always 1 */ ++ unsigned long entry_point; ++ const char os_name[32]; /* For diagnostic purposes only */ ++ const char os_version_str[32]; ++ unsigned long os_version_num; ++ unsigned long expected_ocm_code_start;/* OS Code */ ++ unsigned long expected_ocm_data_end; /* OS Data */ ++ unsigned long expected_ram_start; ++ unsigned long expected_ram_end; ++ unsigned long arch_version; ++}; ++ ++ ++/* ++ * The os_node is only referenced by head.S and should never be modified at ++ * run-time. ++ */ ++asmlinkage const struct os_node _os_node = { ++ .node = { ++ .next = NULL, ++ .name = { "OS" }, ++ .magic = 0x10203040, ++ }, ++ .version = 0x10001, ++ .entry_point = (unsigned long)&_start, ++#if APP_OCM_CODE_SIZE || APP_OCM_DATA_SIZE ++ .expected_ocm_code_start = OCMSTART + APP_OCM_CODE_SIZE, ++ .expected_ocm_data_end = OCMEND - APP_OCM_DATA_SIZE, ++#else ++ .expected_ocm_code_start = OCMEND, ++ .expected_ocm_data_end = OCMEND, ++#endif ++ .os_name = { UTS_SYSNAME }, ++ .os_version_str = { UTS_RELEASE }, ++ .os_version_num = LINUX_VERSION_CODE, ++ .expected_ram_start = CONFIG_KERNELBASE, ++ .expected_ram_end = CONFIG_RAMBASE + CONFIG_RAMSIZE, ++ .arch_version = UBICOM32_ARCH_VERSION ++}; +--- /dev/null ++++ b/arch/ubicom32/kernel/process.c +@@ -0,0 +1,494 @@ ++/* ++ * arch/ubicom32/kernel/process.c ++ * Ubicom32 architecture-dependent process handling. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1995 Hamish Macdonald ++ * ++ * 68060 fixes by Jesper Skov ++ * ++ * uClinux changes ++ * Copyright (C) 2000-2002, David McCullough <davidm@snapgear.com> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++/* ++ * This file handles the architecture-dependent parts of process handling.. ++ */ ++ ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/sched.h> ++#include <linux/kernel.h> ++#include <linux/mm.h> ++#include <linux/smp.h> ++#include <linux/smp_lock.h> ++#include <linux/stddef.h> ++#include <linux/unistd.h> ++#include <linux/ptrace.h> ++#include <linux/slab.h> ++#include <linux/user.h> ++#include <linux/a.out.h> ++#include <linux/interrupt.h> ++#include <linux/reboot.h> ++#include <linux/fs.h> ++#include <linux/pm.h> ++ ++#include <linux/uaccess.h> ++#include <asm/system.h> ++#include <asm/traps.h> ++#include <asm/machdep.h> ++#include <asm/setup.h> ++#include <asm/pgtable.h> ++#include <asm/ip5000.h> ++#include <asm/range-protect.h> ++ ++asmlinkage void ret_from_fork(void); ++ ++void (*pm_power_off)(void) = machine_power_off; ++EXPORT_SYMBOL(pm_power_off); ++ ++/* machine-dependent / hardware-specific power functions */ ++void (*mach_reset)(void); ++void (*mach_halt)(void); ++void (*mach_power_off)(void); ++ ++/* ++ * cpu_idle() ++ * The idle thread. ++ * ++ * Our idle loop suspends and is woken up by a timer interrupt. ++ */ ++void cpu_idle(void) ++{ ++ while (1) { ++ local_irq_disable(); ++ while (!need_resched()) { ++ local_irq_enable(); ++ thread_suspend(); ++ local_irq_disable(); ++ } ++ local_irq_enable(); ++ preempt_enable_no_resched(); ++ schedule(); ++ preempt_disable(); ++ } ++} ++ ++/* ++ * dump_fpu() ++ * ++ * Fill in the fpu structure for a core dump. (just a stub as we don't have ++ * an fpu) ++ */ ++int dump_fpu(struct pt_regs *regs, elf_fpregset_t * fpregs) ++{ ++ return 1; ++} ++ ++/* ++ * machine_restart() ++ * Resets the system. ++ */ ++void machine_restart(char *__unused) ++{ ++ /* ++ * Disable all threads except myself. We can do this ++ * directly without needing to call smp_send_stop ++ * because we have a unique architecture where ++ * one thread can disable one or more other threads. ++ */ ++ thread_disable_others(); ++ ++ /* ++ * Call the hardware-specific machine reset function. ++ */ ++ if (mach_reset) { ++ mach_reset(); ++ } ++ ++ printk(KERN_EMERG "System Restarting\n"); ++ ++ /* ++ * Set watchdog to trigger (after 1ms delay) (12 Mhz is the fixed OSC) ++ */ ++ UBICOM32_IO_TIMER->tkey = TIMER_TKEYVAL; ++ UBICOM32_IO_TIMER->wdcom = UBICOM32_IO_TIMER->mptval + ++ (12000000 / 1000); ++ UBICOM32_IO_TIMER->wdcfg = 0; ++ UBICOM32_IO_TIMER->tkey = 0; ++ ++ /* ++ * Wait for watchdog ++ */ ++ asm volatile ( ++ " move.4 MT_EN, #0 \n\t" ++ " pipe_flush 0 \n\t" ++ ); ++ ++ local_irq_disable(); ++ for (;;) { ++ thread_suspend(); ++ } ++} ++ ++/* ++ * machine_halt() ++ * Halt the machine. ++ * ++ * Similar to machine_power_off, but don't shut off power. Add code ++ * here to freeze the system for e.g. post-mortem debug purpose when ++ * possible. This halt has nothing to do with the idle halt. ++ */ ++void machine_halt(void) ++{ ++ /* ++ * Disable all threads except myself. We can do this ++ * directly without needing to call smp_send_stop ++ * because we have a unique architecture where ++ * one thread can disable one or more other threads. ++ */ ++ thread_disable_others(); ++ ++ /* ++ * Call the hardware-specific machine halt function. ++ */ ++ if (mach_halt) { ++ mach_halt(); ++ } ++ ++ printk(KERN_EMERG "System Halted, OK to turn off power\n"); ++ local_irq_disable(); ++ for (;;) { ++ thread_suspend(); ++ } ++} ++ ++/* ++ * machine_power_off() ++ * Turn the power off, if a power off handler is defined, otherwise, spin ++ * endlessly. ++ */ ++void machine_power_off(void) ++{ ++ /* ++ * Disable all threads except myself. We can do this ++ * directly without needing to call smp_send_stop ++ * because we have a unique architecture where ++ * one thread can disable one or more other threads. ++ */ ++ thread_disable_others(); ++ ++ /* ++ * Call the hardware-specific machine power off function. ++ */ ++ if (mach_power_off) { ++ mach_power_off(); ++ } ++ ++ printk(KERN_EMERG "System Halted, OK to turn off power\n"); ++ local_irq_disable(); ++ for (;;) { ++ thread_suspend(); ++ } ++} ++ ++/* ++ * show_regs() ++ * Print out all of the registers. ++ */ ++void show_regs(struct pt_regs *regs) ++{ ++ unsigned int i; ++ ++ printk(KERN_NOTICE "regs: %p, tid: %d\n", ++ (void *)regs, ++ thread_get_self()); ++ ++ printk(KERN_NOTICE "pc: %08x, previous_pc: %08x\n\n", ++ (unsigned int)regs->pc, ++ (unsigned int)regs->previous_pc); ++ ++ printk(KERN_NOTICE "Data registers\n"); ++ for (i = 0; i < 16; i++) { ++ printk("D%02d: %08x, ", i, (unsigned int)regs->dn[i]); ++ if ((i % 4) == 3) { ++ printk("\n"); ++ } ++ } ++ printk("\n"); ++ ++ printk(KERN_NOTICE "Address registers\n"); ++ for (i = 0; i < 8; i++) { ++ printk("A%02d: %08x, ", i, (unsigned int)regs->an[i]); ++ if ((i % 4) == 3) { ++ printk("\n"); ++ } ++ } ++ printk("\n"); ++ ++ printk(KERN_NOTICE "acc0: %08x-%08x, acc1: %08x-%08x\n", ++ (unsigned int)regs->acc0[1], ++ (unsigned int)regs->acc0[0], ++ (unsigned int)regs->acc1[1], ++ (unsigned int)regs->acc1[0]); ++ ++ printk(KERN_NOTICE "mac_rc16: %08x, source3: %08x\n", ++ (unsigned int)regs->mac_rc16, ++ (unsigned int)regs->source3); ++ ++ printk(KERN_NOTICE "inst_cnt: %08x, csr: %08x\n", ++ (unsigned int)regs->inst_cnt, ++ (unsigned int)regs->csr); ++ ++ printk(KERN_NOTICE "int_mask0: %08x, int_mask1: %08x\n", ++ (unsigned int)regs->int_mask0, ++ (unsigned int)regs->int_mask1); ++ ++ printk(KERN_NOTICE "frame_type: %d, nesting_level: %d, thread_type %d\n\n", ++ (int)regs->frame_type, ++ (int)regs->nesting_level, ++ (int)regs->thread_type); ++} ++ ++/* ++ * kernel_thread_helper() ++ * On execution d0 will be 0, d1 will be the argument to be passed to the ++ * kernel function. d2 contains the kernel function that needs to get ++ * called. d3 will contain address to do_exit which need to get moved ++ * into a5. On return from fork the child thread d0 will be 0. We call ++ * this dummy function which in turn loads the argument ++ */ ++asmlinkage void kernel_thread_helper(void); ++ ++/* ++ * kernel_thread() ++ * Create a kernel thread ++ */ ++int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) ++{ ++ struct pt_regs regs; ++ ++ memset(®s, 0, sizeof(regs)); ++ ++ regs.dn[1] = (unsigned long)arg; ++ regs.dn[2] = (unsigned long)fn; ++ regs.dn[3] = (unsigned long)do_exit; ++ regs.an[5] = (unsigned long)kernel_thread_helper; ++ regs.pc = (unsigned long)kernel_thread_helper; ++ regs.nesting_level = 0; ++ regs.thread_type = KERNEL_THREAD; ++ ++ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, ++ 0, ®s, 0, NULL, NULL); ++} ++EXPORT_SYMBOL(kernel_thread); ++ ++/* ++ * flush_thread() ++ * XXX todo ++ */ ++void flush_thread(void) ++{ ++ /* XXX todo */ ++} ++ ++/* ++ * sys_fork() ++ * Not implemented on no-mmu. ++ */ ++asmlinkage int sys_fork(struct pt_regs *regs) ++{ ++ /* fork almost works, enough to trick you into looking elsewhere :-( */ ++ return -EINVAL; ++} ++ ++/* ++ * sys_vfork() ++ * By the time we get here, the non-volatile registers have also been saved ++ * on the stack. We do some ugly pointer stuff here.. (see also copy_thread ++ * which does context copy). ++ */ ++asmlinkage int sys_vfork(struct pt_regs *regs) ++{ ++ unsigned long old_sp = regs->an[7]; ++ unsigned long old_a5 = regs->an[5]; ++ unsigned long old_return_address; ++ long do_fork_return; ++ ++ /* ++ * Read the old retrun address from the stack. ++ */ ++ if (copy_from_user(&old_return_address, ++ (void *)old_sp, sizeof(unsigned long))) { ++ force_sig(SIGSEGV, current); ++ return 0; ++ } ++ ++ /* ++ * Pop the vfork call frame by setting a5 and pc to the old_return ++ * address and incrementing the stack pointer by 4. ++ */ ++ regs->an[5] = old_return_address; ++ regs->pc = old_return_address; ++ regs->an[7] += 4; ++ ++ do_fork_return = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, ++ regs->an[7], regs, 0, NULL, NULL); ++ ++ /* ++ * Now we have to test if the return code is an error. If it is an error ++ * then restore the frame and we will execute error processing in user ++ * space. Other wise the child and the parent will return to the correct ++ * places. ++ */ ++ if ((unsigned long)(do_fork_return) >= (unsigned long)(-125)) { ++ /* ++ * Error case. We need to restore the frame. ++ */ ++ regs->an[5] = old_a5; ++ regs->pc = old_a5; ++ regs->an[7] = old_sp; ++ } ++ ++ return do_fork_return; ++} ++ ++/* ++ * sys_clone() ++ * creates a child thread. ++ */ ++asmlinkage int sys_clone(unsigned long clone_flags, ++ unsigned long newsp, ++ struct pt_regs *regs) ++{ ++ if (!newsp) ++ newsp = regs->an[7]; ++ return do_fork(clone_flags, newsp, regs, 0, ++ NULL, NULL); ++} ++ ++/* ++ * copy_thread() ++ * low level thread copy, only used by do_fork in kernel/fork.c ++ */ ++int copy_thread(int nr, unsigned long clone_flags, ++ unsigned long usp, unsigned long topstk, ++ struct task_struct *p, struct pt_regs *regs) ++{ ++ struct pt_regs *childregs; ++ ++ childregs = (struct pt_regs *) ++ (task_stack_page(p) + THREAD_SIZE - 8) - 1; ++ ++ *childregs = *regs; ++ ++ /* ++ * Set return value for child to be 0. ++ */ ++ childregs->dn[0] = 0; ++ ++ if (usp) ++ childregs->an[7] = usp; ++ else ++ childregs->an[7] = (unsigned long)task_stack_page(p) + ++ THREAD_SIZE - 8; ++ ++ /* ++ * Set up the switch_to frame to return to "ret_from_fork" ++ */ ++ p->thread.a5 = (unsigned long)ret_from_fork; ++ p->thread.sp = (unsigned long)childregs; ++ ++ return 0; ++} ++ ++/* ++ * sys_execve() ++ * executes a new program. ++ */ ++asmlinkage int sys_execve(char *name, char **argv, ++ char **envp, struct pt_regs *regs) ++{ ++ int error; ++ char *filename; ++ ++ lock_kernel(); ++ filename = getname(name); ++ error = PTR_ERR(filename); ++ if (IS_ERR(filename)) ++ goto out; ++ error = do_execve(filename, argv, envp, regs); ++ putname(filename); ++ asm (" .global sys_execve_complete\n" ++ " sys_execve_complete:"); ++out: ++ unlock_kernel(); ++ return error; ++} ++ ++/* ++ * Return saved PC of a blocked thread. ++ */ ++unsigned long thread_saved_pc(struct task_struct *tsk) ++{ ++ return tsk->thread.a5; ++} ++ ++ ++unsigned long get_wchan(struct task_struct *p) ++{ ++ unsigned long pc; ++ ++ /* ++ * If we don't have a process, or it is not the current ++ * one or not RUNNING, it makes no sense to ask for a ++ * wchan. ++ */ ++ if (!p || p == current || p->state == TASK_RUNNING) ++ return 0; ++ ++ /* ++ * TODO: If the process is in the middle of schedule, we ++ * are supposed to do something different but for now we ++ * will return the same thing in both situations. ++ */ ++ pc = thread_saved_pc(p); ++ if (in_sched_functions(pc)) ++ return pc; ++ return pc; ++} ++ ++/* ++ * __switch_to is the function that implements the contex save and ++ * switch within the kernel. Since this is a function call very few ++ * registers have to be saved to pull this off. d0 holds prev and we ++ * want to preserve it. prev_switch is a pointer to task->thread ++ * structure. This is where we will save the register state. next_switch ++ * is pointer to the next task's thread structure that holds the ++ * registers. ++ */ ++asmlinkage void *__switch_to(struct task_struct *prev, ++ struct thread_struct *prev_switch, ++ struct thread_struct *next_switch) ++ __attribute__((naked)); +--- /dev/null ++++ b/arch/ubicom32/kernel/processor.c +@@ -0,0 +1,258 @@ ++/* ++ * arch/ubicom32/kernel/processor.c ++ * Ubicom32 architecture processor info implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/profile.h> ++#include <linux/clocksource.h> ++#include <linux/types.h> ++#include <linux/seq_file.h> ++#include <linux/delay.h> ++#include <linux/cpu.h> ++#include <asm/devtree.h> ++#include <asm/processor.h> ++#include <asm/cpu.h> ++ ++struct procnode { ++ struct devtree_node dn; ++ unsigned int threads; ++ unsigned int timers; ++ unsigned int frequency; ++ unsigned int interrupt0; ++ unsigned int interrupt1; ++ void *socm; ++ void *eocm; ++ void *sdram; ++ void *edram; ++}; ++ ++struct procnode *pn; ++ ++/* ++ * show_processorinfo() ++ * Print the actual processor information. ++ */ ++static void show_processorinfo(struct seq_file *m) ++{ ++ char *cpu, *mmu, *fpu; ++ unsigned int clockfreq; ++ unsigned int chipid; ++ ++ cpu = CPU; ++ mmu = "none"; ++ fpu = "none"; ++ ++ asm volatile ( ++ "move.4 %0, CHIP_ID \n\t" ++ : "=r" (chipid) ++ ); ++ ++ /* ++ * General Processor Information. ++ */ ++ seq_printf(m, "Vendor:\t\t%s\n", "Ubicom"); ++ seq_printf(m, "CPU:\t\t%s\n", cpu); ++ seq_printf(m, "MMU:\t\t%s\n", mmu); ++ seq_printf(m, "FPU:\t\t%s\n", fpu); ++ seq_printf(m, "Arch:\t\t%hx\n", chipid >> 16); ++ seq_printf(m, "Rev:\t\t%hx\n", (chipid & 0xffff)); ++ ++ /* ++ * Now compute the clock frequency in Mhz. ++ */ ++ clockfreq = processor_frequency(); ++ seq_printf(m, "Clock Freq:\t%u.%1u MHz\n", ++ clockfreq / 1000000, ++ (clockfreq / 100000) % 10); ++ seq_printf(m, "BogoMips:\t%lu.%02lu\n", ++ (loops_per_jiffy * HZ) / 500000, ++ ((loops_per_jiffy * HZ) / 5000) % 100); ++ seq_printf(m, "Calibration:\t%lu loops\n", (loops_per_jiffy * HZ)); ++} ++ ++/* ++ * show_cpuinfo() ++ * Get CPU information for use by the procfs. ++ */ ++static int show_cpuinfo(struct seq_file *m, void *v) ++{ ++ unsigned long n = (unsigned long)v - 1; ++ ++#if defined(CONFIG_SMP) ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, n); ++#endif ++ ++ /* ++ * Print the general processor information on the first ++ * call. ++ */ ++ if (n == 0) { ++ show_processorinfo(m); ++ } ++ ++#if defined(CONFIG_SMP) ++ /* ++ * For each hwthread, print if this hwthread is running Linux ++ * or is an I/O thread. ++ */ ++ if (cpu_isset(n, cpu_online_map)) { ++ seq_printf(m, "cpu[%02lu]:\tthread id - %lu\n", n, p->tid); ++ } else { ++ seq_printf(m, "cpu[%02lu]:\toff-line\n", n); ++ } ++#endif ++ return 0; ++ ++} ++ ++static void *c_start(struct seq_file *m, loff_t *pos) ++{ ++ unsigned long i = *pos; ++ ++ return i < NR_CPUS ? (void *)(i + 1) : NULL; ++} ++ ++static void *c_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ ++*pos; ++ return c_start(m, pos); ++} ++ ++static void c_stop(struct seq_file *m, void *v) ++{ ++} ++ ++const struct seq_operations cpuinfo_op = { ++ .start = c_start, ++ .next = c_next, ++ .stop = c_stop, ++ .show = show_cpuinfo, ++}; ++ ++/* ++ * processor_timers() ++ * Returns the timers available to Linux. ++ */ ++unsigned int processor_timers(void) ++{ ++ if (!pn) { ++ return 0; ++ } ++ return pn->timers; ++} ++ ++/* ++ * processor_threads() ++ * Returns the threads available to Linux. ++ */ ++unsigned int processor_threads(void) ++{ ++ if (!pn) { ++ return 0; ++ } ++ return pn->threads; ++} ++ ++/* ++ * processor_frequency() ++ * Returns the frequency of the system clock. ++ */ ++unsigned int processor_frequency(void) ++{ ++ if (!pn) { ++ return 0; ++ } ++ return pn->frequency; ++} ++EXPORT_SYMBOL(processor_frequency); ++ ++/* ++ * processor_interrupts() ++ * Return the interrupts that are setup at boot time. ++ */ ++int processor_interrupts(unsigned int *int0, unsigned int *int1) ++{ ++ if (!pn) { ++ return -EFAULT; ++ } ++ ++ if (int0) { ++ *int0 = pn->interrupt0; ++ } ++ ++ if (int1) { ++ *int1 = pn->interrupt1; ++ } ++ return 0; ++} ++ ++/* ++ * processor_ocm() ++ * Returns the OCM available to Linux. ++ */ ++int processor_ocm(void **socm, void **eocm) ++{ ++ if (!pn) { ++ return -ENOSYS; ++ } ++ ++ *socm = pn->socm; ++ *eocm = pn->eocm; ++ return 0; ++} ++ ++/* ++ * processor_dram() ++ * Returns the dram available to Linux. ++ */ ++int processor_dram(void **sdram, void **edram) ++{ ++ if (!pn) { ++ return -ENOSYS; ++ } ++ ++ *sdram = pn->sdram; ++ *edram = pn->edram; ++ return 0; ++} ++ ++void __init processor_init(void) ++{ ++ /* ++ * If we do not have a trap node in the device tree, we leave the fault ++ * handling to the underlying hardware. ++ */ ++ pn = (struct procnode *)devtree_find_node("processor"); ++ if (!pn) { ++ printk(KERN_WARNING "processor node not found\n"); ++ return; ++ } ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/ptrace.c +@@ -0,0 +1,275 @@ ++/* ++ * arch/ubicom32/kernel/ptrace.c ++ * Ubicom32 architecture ptrace implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * (C) 1994 by Hamish Macdonald ++ * Taken from linux/kernel/ptrace.c and modified for M680x0. ++ * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/mm.h> ++#include <linux/smp.h> ++#include <linux/errno.h> ++#include <linux/ptrace.h> ++#include <linux/user.h> ++#include <linux/signal.h> ++#include <linux/uaccess.h> ++ ++#include <asm/page.h> ++#include <asm/pgtable.h> ++#include <asm/system.h> ++#include <asm/cacheflush.h> ++#include <asm/processor.h> ++ ++/* ++ * ptrace_getregs() ++ * ++ * Get all user integer registers. ++ */ ++static inline int ptrace_getregs(struct task_struct *task, void __user *uregs) ++{ ++ struct pt_regs *regs = task_pt_regs(task); ++ return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0; ++} ++ ++/* ++ * ptrace_get_reg() ++ * ++ * Get contents of register REGNO in task TASK. ++ */ ++static unsigned long ptrace_get_reg(struct task_struct *task, int regno) ++{ ++ if (regno < sizeof(struct pt_regs)) { ++ struct pt_regs *pt_regs = task_pt_regs(task); ++ return *(unsigned long *)((long) pt_regs + regno); ++ } ++ ++ return -EIO; ++} ++ ++/* ++ * ptrace_put_reg() ++ * Write contents of register REGNO in task TASK. ++ */ ++static int ptrace_put_reg(struct task_struct *task, int regno, ++ unsigned long data) ++{ ++ if (regno <= sizeof(struct pt_regs) && regno != PT_FRAME_TYPE) { ++ struct pt_regs *pt_regs = task_pt_regs(task); ++ *(unsigned long *)((long) pt_regs + regno) = data; ++ return 0; ++ } ++ return -EIO; ++} ++ ++/* ++ * ptrace_disable_single_step() ++ * Disable Single Step ++ */ ++static int ptrace_disable_single_step(struct task_struct *task) ++{ ++ /* ++ * Single Step not yet implemented, so must always be disabled ++ */ ++ return 0; ++} ++ ++/* ++ * ptrace_disable() ++ * Make sure the single step bit is not set. ++ * Called by kernel/ptrace.c when detaching.. ++ */ ++void ptrace_disable(struct task_struct *child) ++{ ++ ptrace_disable_single_step(child); ++} ++ ++/* ++ * arch_ptrace() ++ * architecture specific ptrace routine. ++ */ ++long arch_ptrace(struct task_struct *child, long request, long addr, long data) ++{ ++ int ret; ++ switch (request) { ++ /* when I and D space are separate, these will need to be fixed. */ ++ case PTRACE_PEEKTEXT: /* read word at location addr. */ ++ case PTRACE_PEEKDATA: ++ ret = generic_ptrace_peekdata(child, addr, data); ++ break; ++ ++ /* read the word at location addr in the USER area. */ ++ case PTRACE_PEEKUSR: { ++ unsigned long tmp; ++ ++ ret = -EIO; ++ if (((unsigned long) addr > PT_INTERP_FDPIC_LOADMAP) ++ || (addr & 3)) ++ break; ++ ++ tmp = 0; /* Default return condition */ ++ ++ ret = -EIO; ++ if (addr < sizeof(struct pt_regs)) { ++ tmp = ptrace_get_reg(child, addr); ++ } else if (addr == PT_TEXT_ADDR) { ++ tmp = child->mm->start_code; ++ } else if (addr == PT_TEXT_END_ADDR) { ++ tmp = child->mm->end_code; ++ } else if (addr == PT_DATA_ADDR) { ++ tmp = child->mm->start_data; ++ } else if (addr == PT_EXEC_FDPIC_LOADMAP) { ++#ifdef CONFIG_BINFMT_ELF_FDPIC ++ tmp = child->mm->context.exec_fdpic_loadmap; ++#endif ++ } else if (addr == PT_INTERP_FDPIC_LOADMAP) { ++#ifdef CONFIG_BINFMT_ELF_FDPIC ++ tmp = child->mm->context.interp_fdpic_loadmap; ++#endif ++ } else { ++ break; ++ } ++ ++ ret = put_user(tmp, (unsigned long *)data); ++ break; ++ } ++ ++ case PTRACE_POKETEXT: /* write the word at location addr. */ ++ case PTRACE_POKEDATA: ++ ret = generic_ptrace_pokedata(child, addr, data); ++ ++ /* ++ * If we just changed some code so we need to ++ * correct the caches ++ */ ++ if (request == PTRACE_POKETEXT && ret == 0) { ++ flush_icache_range(addr, addr + 4); ++ } ++ break; ++ ++ case PTRACE_POKEUSR: /* write the word at location addr ++ * in the USER area */ ++ ret = -EIO; ++ ++ if (((unsigned long) addr > PT_DATA_ADDR) || (addr & 3)) ++ break; ++ ++ if (addr < sizeof(struct pt_regs)) { ++ ret = ptrace_put_reg(child, addr, data); ++ } ++ break; ++ ++ case PTRACE_SYSCALL: /* continue and stop at next (return from) ++ * syscall */ ++ case PTRACE_CONT: { /* restart after signal. */ ++ ++ ret = -EIO; ++ if (!valid_signal(data)) ++ break; ++ if (request == PTRACE_SYSCALL) ++ set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); ++ else ++ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); ++ child->exit_code = data; ++ /* make sure the single step bit is not set. */ ++ ptrace_disable_single_step(child); ++ wake_up_process(child); ++ ret = 0; ++ break; ++ } ++ ++ /* ++ * make the child exit. Best I can do is send it a sigkill. ++ * perhaps it should be put in the status that it wants to exit. ++ */ ++ case PTRACE_KILL: { ++ ret = 0; ++ if (child->exit_state == EXIT_ZOMBIE) /* already dead */ ++ break; ++ child->exit_code = SIGKILL; ++ /* make sure the single step bit is not set. */ ++ ptrace_disable_single_step(child); ++ wake_up_process(child); ++ break; ++ } ++ ++ case PTRACE_DETACH: /* detach a process that was attached. */ ++ ret = ptrace_detach(child, data); ++ break; ++ ++ case PTRACE_GETREGS: /* Get all gp regs from the child. */ ++ ptrace_getregs(child, (unsigned long *)data); ++ ret = 0; ++ break; ++ ++ case PTRACE_SETREGS: { /* Set all gp regs in the child. */ ++ int i; ++ unsigned long tmp; ++ int count = sizeof(struct pt_regs) / sizeof(unsigned long); ++ for (i = 0; i < count; i++) { ++ if (get_user(tmp, (unsigned long *) data)) { ++ ret = -EFAULT; ++ break; ++ } ++ ptrace_put_reg(child, sizeof(unsigned long) * i, tmp); ++ data += sizeof(long); ++ } ++ ret = 0; ++ break; ++ } ++ ++ default: ++ return ptrace_request(child, request, addr, data); ++ break; ++ } ++ return ret; ++} ++/* ++ * syscall_trace ++ * ++ * called by syscall enter/exit when the TIF_SYSCALL_TRACE bit is set. ++ */ ++asmlinkage void syscall_trace(void) ++{ ++ struct task_struct *cur = current; ++ if (!test_thread_flag(TIF_SYSCALL_TRACE)) ++ return; ++ if (!(cur->ptrace & PT_PTRACED)) ++ return; ++ ptrace_notify(SIGTRAP | ((cur->ptrace & PT_TRACESYSGOOD) ++ ? 0x80 : 0)); ++ /* ++ * this isn't the same as continuing with a signal, but it will do ++ * for normal use. strace only continues with a signal if the ++ * stopping signal is not SIGTRAP. -brl ++ */ ++ if (cur->exit_code) { ++ send_sig(cur->exit_code, current, 1); ++ current->exit_code = 0; ++ } ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/semaphore.c +@@ -0,0 +1,159 @@ ++/* ++ * arch/ubicom32/kernel/semaphore.c ++ * Ubicom32 architecture semaphore implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++/* ++ * Generic semaphore code. Buyer beware. Do your own ++ * specific changes in <asm/semaphore-helper.h> ++ */ ++ ++#include <linux/sched.h> ++#include <linux/err.h> ++#include <linux/init.h> ++#include <asm/semaphore-helper.h> ++ ++#ifndef CONFIG_RMW_INSNS ++spinlock_t semaphore_wake_lock; ++#endif ++ ++/* ++ * Semaphores are implemented using a two-way counter: ++ * The "count" variable is decremented for each process ++ * that tries to sleep, while the "waking" variable is ++ * incremented when the "up()" code goes to wake up waiting ++ * processes. ++ * ++ * Notably, the inline "up()" and "down()" functions can ++ * efficiently test if they need to do any extra work (up ++ * needs to do something only if count was negative before ++ * the increment operation. ++ * ++ * waking_non_zero() (from asm/semaphore.h) must execute ++ * atomically. ++ * ++ * When __up() is called, the count was negative before ++ * incrementing it, and we need to wake up somebody. ++ * ++ * This routine adds one to the count of processes that need to ++ * wake up and exit. ALL waiting processes actually wake up but ++ * only the one that gets to the "waking" field first will gate ++ * through and acquire the semaphore. The others will go back ++ * to sleep. ++ * ++ * Note that these functions are only called when there is ++ * contention on the lock, and as such all this is the ++ * "non-critical" part of the whole semaphore business. The ++ * critical part is the inline stuff in <asm/semaphore.h> ++ * where we want to avoid any extra jumps and calls. ++ */ ++void __up(struct semaphore *sem) ++{ ++ wake_one_more(sem); ++ wake_up(&sem->wait); ++} ++ ++/* ++ * Perform the "down" function. Return zero for semaphore acquired, ++ * return negative for signalled out of the function. ++ * ++ * If called from __down, the return is ignored and the wait loop is ++ * not interruptible. This means that a task waiting on a semaphore ++ * using "down()" cannot be killed until someone does an "up()" on ++ * the semaphore. ++ * ++ * If called from __down_interruptible, the return value gets checked ++ * upon return. If the return value is negative then the task continues ++ * with the negative value in the return register (it can be tested by ++ * the caller). ++ * ++ * Either form may be used in conjunction with "up()". ++ * ++ */ ++ ++ ++#define DOWN_HEAD(task_state) \ ++ \ ++ \ ++ current->state = (task_state); \ ++ add_wait_queue(&sem->wait, &wait); \ ++ \ ++ /* \ ++ * Ok, we're set up. sem->count is known to be less than zero \ ++ * so we must wait. \ ++ * \ ++ * We can let go the lock for purposes of waiting. \ ++ * We re-acquire it after awaking so as to protect \ ++ * all semaphore operations. \ ++ * \ ++ * If "up()" is called before we call waking_non_zero() then \ ++ * we will catch it right away. If it is called later then \ ++ * we will have to go through a wakeup cycle to catch it. \ ++ * \ ++ * Multiple waiters contend for the semaphore lock to see \ ++ * who gets to gate through and who has to wait some more. \ ++ */ \ ++ for (;;) { ++ ++#define DOWN_TAIL(task_state) \ ++ current->state = (task_state); \ ++ } \ ++ current->state = TASK_RUNNING; \ ++ remove_wait_queue(&sem->wait, &wait); ++ ++void __sched __down(struct semaphore *sem) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ DOWN_HEAD(TASK_UNINTERRUPTIBLE) ++ if (waking_non_zero(sem)) ++ break; ++ schedule(); ++ DOWN_TAIL(TASK_UNINTERRUPTIBLE) ++} ++ ++int __sched __down_interruptible(struct semaphore *sem) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ int ret = 0; ++ ++ DOWN_HEAD(TASK_INTERRUPTIBLE) ++ ++ ret = waking_non_zero_interruptible(sem, current); ++ if (ret) { ++ if (ret == 1) ++ /* ret != 0 only if we get interrupted -arca */ ++ ret = 0; ++ break; ++ } ++ schedule(); ++ DOWN_TAIL(TASK_INTERRUPTIBLE) ++ return ret; ++} ++ ++int __down_trylock(struct semaphore *sem) ++{ ++ return waking_non_zero_trylock(sem); ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/setup.c +@@ -0,0 +1,189 @@ ++/* ++ * arch/ubicom32/kernel/setup.c ++ * Ubicom32 architecture-dependent parts of system setup. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1999-2007 Greg Ungerer (gerg@snapgear.com) ++ * Copyright (C) 1998,1999 D. Jeff Dionne <jeff@uClinux.org> ++ * Copyleft ()) 2000 James D. Schettine {james@telos-systems.com} ++ * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com> ++ * Copyright (C) 1995 Hamish Macdonald ++ * Copyright (C) 2000 Lineo Inc. (www.lineo.com) ++ * Copyright (C) 2001 Lineo, Inc. <www.lineo.com> ++ * 68VZ328 Fixes/support Evan Stawnyczy <e@lineo.ca> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/fb.h> ++#include <linux/module.h> ++#include <linux/console.h> ++#include <linux/errno.h> ++#include <linux/string.h> ++#include <linux/bootmem.h> ++#include <linux/seq_file.h> ++#include <linux/init.h> ++ ++#include <asm/devtree.h> ++#include <asm/setup.h> ++#include <asm/irq.h> ++#include <asm/machdep.h> ++#include <asm/pgtable.h> ++#include <asm/pgalloc.h> ++#include <asm/ubicom32-common.h> ++#include <asm/processor.h> ++#include <asm/bootargs.h> ++#include <asm/thread.h> ++ ++unsigned long memory_start; ++EXPORT_SYMBOL(memory_start); ++ ++unsigned long memory_end; ++EXPORT_SYMBOL(memory_end); ++ ++static char __initdata command_line[COMMAND_LINE_SIZE]; ++#ifdef CONFIG_CMDLINE_BOOL ++static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE; ++#endif ++ ++extern int _stext, _etext, _sdata, _edata, _sbss, _ebss, _end; ++ ++/* ++ * setup_arch() ++ * Setup the architecture dependent portions of the system. ++ */ ++void __init setup_arch(char **cmdline_p) ++{ ++ int bootmap_size; ++ ++ processor_init(); ++ bootargs_init(); ++ ++ /* ++ * TODO: The memory regions should really come from the ++ * processor node. ++ */ ++ memory_start = PAGE_ALIGN(((unsigned long)&_end)); ++ memory_end = CONFIG_RAMBASE+CONFIG_RAMSIZE; ++ ++ init_mm.start_code = (unsigned long) &_stext; ++ init_mm.end_code = (unsigned long) &_etext; ++ init_mm.end_data = (unsigned long) &_edata; ++ init_mm.brk = (unsigned long) 0; ++ ++ strlcpy(boot_command_line, bootargs_get_cmdline(), COMMAND_LINE_SIZE); ++ ++#ifdef CONFIG_CMDLINE_BOOL ++#ifdef CONFIG_CMDLINE_OVERRIDE ++ strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); ++#else ++ if (builtin_cmdline[0]) { ++ /* append boot loader cmdline to builtin */ ++ strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE); ++ strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE); ++ strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); ++ } ++#endif ++#endif ++ ++ strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); ++ *cmdline_p = command_line; ++ ++ parse_early_param(); ++ ++ printk(KERN_INFO "%s Processor, Ubicom, Inc. <www.ubicom.com>\n", CPU); ++ ++#if defined(DEBUG) ++ printk(KERN_DEBUG "KERNEL -> TEXT=0x%06x-0x%06x DATA=0x%06x-0x%06x " ++ "BSS=0x%06x-0x%06x\n", (int) &_stext, (int) &_etext, ++ (int) &_sdata, (int) &_edata, ++ (int) &_sbss, (int) &_ebss); ++ printk(KERN_DEBUG "MEMORY -> ROMFS=0x%06x-0x%06x MEM=0x%06x-0x%06x\n ", ++ (int) &_ebss, (int) memory_start, ++ (int) memory_start, (int) memory_end); ++#endif ++ ++ /* Keep a copy of command line */ ++ *cmdline_p = &command_line[0]; ++ memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); ++ boot_command_line[COMMAND_LINE_SIZE-1] = 0; ++ ++#ifdef DEBUG ++ if (strlen(*cmdline_p)) ++ printk(KERN_DEBUG "Command line: '%s'\n", *cmdline_p); ++#endif ++ ++#if defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_DUMMY_CONSOLE) ++ conswitchp = &dummy_con; ++#endif ++ ++ /* ++ * If we have a device tree, see if we have the nodes we need. ++ */ ++ if (devtree) { ++ devtree_print(); ++ } ++ ++ /* ++ * From the arm initialization comment: ++ * ++ * This doesn't seem to be used by the Linux memory manager any ++ * more, but is used by ll_rw_block. If we can get rid of it, we ++ * also get rid of some of the stuff above as well. ++ * ++ * Note: max_low_pfn and max_pfn reflect the number of _pages_ in ++ * the system, not the maximum PFN. ++ */ ++ max_pfn = max_low_pfn = (memory_end - PAGE_OFFSET) >> PAGE_SHIFT; ++ ++ /* ++ * Give all the memory to the bootmap allocator, tell it to put the ++ * boot mem_map at the start of memory. ++ */ ++ bootmap_size = init_bootmem_node( ++ NODE_DATA(0), ++ memory_start >> PAGE_SHIFT, /* map goes here */ ++ PAGE_OFFSET >> PAGE_SHIFT, /* 0 on coldfire */ ++ memory_end >> PAGE_SHIFT); ++ /* ++ * Free the usable memory, we have to make sure we do not free ++ * the bootmem bitmap so we then reserve it after freeing it :-) ++ */ ++ free_bootmem(memory_start, memory_end - memory_start); ++ reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); ++ ++ /* ++ * Get kmalloc into gear. ++ */ ++ paging_init(); ++ ++ /* ++ * Fix up the thread_info structure, indicate this is a mainline Linux ++ * thread and setup the sw_ksp(). ++ */ ++ sw_ksp[thread_get_self()] = (unsigned int) current_thread_info(); ++ thread_set_mainline(thread_get_self()); ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/signal.c +@@ -0,0 +1,458 @@ ++/* ++ * arch/ubicom32/kernel/signal.c ++ * Ubicom32 architecture signal handling implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1991, 1992 Linus Torvalds ++ * Linux/m68k support by Hamish Macdonald ++ * 68060 fixes by Jesper Skov ++ * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab ++ * mathemu support by Roman Zippel ++ * ++roman (07/09/96): implemented signal stacks ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * ++ * mathemu support by Roman Zippel ++ * (Note: fpstate in the signal context is completely ignored for the emulator ++ * and the internal floating point format is put on stack) ++ * ++ * ++roman (07/09/96): implemented signal stacks (specially for tosemu on ++ * Atari :-) Current limitation: Only one sigstack can be active at one time. ++ * If a second signal with SA_ONSTACK set arrives while working on a sigstack, ++ * SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested ++ * signal handlers! ++ */ ++ ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/mm.h> ++#include <linux/kernel.h> ++#include <linux/signal.h> ++#include <linux/syscalls.h> ++#include <linux/errno.h> ++#include <linux/wait.h> ++#include <linux/ptrace.h> ++#include <linux/unistd.h> ++#include <linux/stddef.h> ++#include <linux/highuid.h> ++#include <linux/tty.h> ++#include <linux/personality.h> ++#include <linux/binfmts.h> ++ ++#include <asm/setup.h> ++#include <asm/uaccess.h> ++#include <asm/pgtable.h> ++#include <asm/traps.h> ++#include <asm/ucontext.h> ++ ++#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) ++ ++/* ++ * asm signal return handlers. ++ */ ++void ret_from_user_signal(void); ++void ret_from_user_rt_signal(void); ++asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs); ++ ++/* ++ * Common signal suspend implementation ++ */ ++static int signal_suspend(sigset_t *saveset, struct pt_regs *regs) ++{ ++ regs->dn[0] = -EINTR; ++ while (1) { ++ current->state = TASK_INTERRUPTIBLE; ++ schedule(); ++ if (!do_signal(saveset, regs)) { ++ continue; ++ } ++ /* ++ * If the current frame type is a signal trampoline we are ++ * actually going to call the signal handler so we return the ++ * desired d0 as the return value. ++ */ ++ if (regs->frame_type == UBICOM32_FRAME_TYPE_SIGTRAMP) { ++ return regs->dn[0]; ++ } ++ return -EINTR; ++ } ++ /* ++ * Should never get here ++ */ ++ BUG(); ++ return 0; ++} ++ ++/* ++ * Atomically swap in the new signal mask, and wait for a signal. ++ */ ++asmlinkage int do_sigsuspend(struct pt_regs *regs) ++{ ++ old_sigset_t mask = regs->dn[0]; ++ sigset_t saveset; ++ ++ mask &= _BLOCKABLE; ++ spin_lock_irq(¤t->sighand->siglock); ++ saveset = current->blocked; ++ siginitset(¤t->blocked, mask); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++ ++ /* ++ * Call common handler ++ */ ++ return signal_suspend(&saveset, regs); ++} ++ ++asmlinkage int ++do_rt_sigsuspend(struct pt_regs *regs) ++{ ++ sigset_t *unewset = (sigset_t *)regs->dn[0]; ++ size_t sigsetsize = (size_t)regs->dn[1]; ++ sigset_t saveset, newset; ++ ++ /* XXX: Don't preclude handling different sized sigset_t's. */ ++ if (sigsetsize != sizeof(sigset_t)) ++ return -EINVAL; ++ ++ if (copy_from_user(&newset, unewset, sizeof(newset))) ++ return -EFAULT; ++ sigdelsetmask(&newset, ~_BLOCKABLE); ++ ++ spin_lock_irq(¤t->sighand->siglock); ++ saveset = current->blocked; ++ current->blocked = newset; ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++ ++ /* ++ * Call common handler ++ */ ++ return signal_suspend(&saveset, regs); ++} ++ ++asmlinkage int ++sys_sigaction(int sig, const struct old_sigaction *act, ++ struct old_sigaction *oact) ++{ ++ struct k_sigaction new_ka, old_ka; ++ int ret; ++ ++ if (act) { ++ old_sigset_t mask; ++ if (!access_ok(VERIFY_READ, act, sizeof(*act)) || ++ __get_user(new_ka.sa.sa_handler, &act->sa_handler) || ++ __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) ++ return -EFAULT; ++ __get_user(new_ka.sa.sa_flags, &act->sa_flags); ++ __get_user(mask, &act->sa_mask); ++ siginitset(&new_ka.sa.sa_mask, mask); ++ } ++ ++ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); ++ ++ if (!ret && oact) { ++ if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || ++ __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || ++ __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) ++ return -EFAULT; ++ __put_user(old_ka.sa.sa_flags, &oact->sa_flags); ++ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); ++ } ++ ++ return ret; ++} ++ ++asmlinkage int ++do_sys_sigaltstack(struct pt_regs *regs) ++{ ++ const stack_t *uss = (stack_t *) regs->dn[0]; ++ stack_t *uoss = (stack_t *)regs->dn[1]; ++ return do_sigaltstack(uss, uoss, regs->an[7]); ++} ++ ++/* ++ * fdpic_func_descriptor describes sa_handler when the application is FDPIC ++ */ ++struct fdpic_func_descriptor { ++ unsigned long text; ++ unsigned long GOT; ++}; ++ ++/* ++ * rt_sigframe is stored on the user stack immediately before (above) ++ * the signal handlers stack. ++ */ ++struct rt_sigframe ++{ ++ unsigned long syscall_number; /* This holds __NR_rt_sigreturn. */ ++ unsigned long restore_all_regs; /* This field gets set to 1 if the frame ++ * type is TRAP or INTERRUPT. */ ++ siginfo_t *info; ++ struct ucontext uc; ++ int sig; ++ void *pretcode; ++}; ++ ++/* ++ * Do a signal return; undo the signal stack. ++ */ ++asmlinkage int do_sigreturn(unsigned long __unused) ++{ ++ BUG(); ++ return 0; ++} ++ ++asmlinkage int do_rt_sigreturn(struct pt_regs *regs) ++{ ++ unsigned long usp = regs->an[7]; ++ struct rt_sigframe *frame = (struct rt_sigframe *)(usp); ++ sigset_t set; ++ ++ if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) ++ goto badframe; ++ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) ++ goto badframe; ++ ++ sigdelsetmask(&set, ~_BLOCKABLE); ++ spin_lock_irq(¤t->sighand->siglock); ++ current->blocked = set; ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++ ++ if (copy_from_user(regs, &frame->uc.uc_mcontext, sizeof(struct pt_regs))) ++ goto badframe; ++ return regs->dn[0]; ++ ++badframe: ++ force_sig(SIGSEGV, current); ++ return 0; ++} ++ ++static inline void * ++get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) ++{ ++ unsigned long usp; ++ ++ /* Default to using normal stack. */ ++ usp = regs->an[7]; ++ ++ /* This is the X/Open sanctioned signal stack switching. */ ++ if (ka->sa.sa_flags & SA_ONSTACK) { ++ if (!sas_ss_flags(usp)) ++ usp = current->sas_ss_sp + current->sas_ss_size; ++ } ++ return (void *)((usp - frame_size) & ~0x3); ++} ++ ++/* ++ * signal_trampoline: Defined in ubicom32_syscall.S ++ */ ++asmlinkage void signal_trampoline(void)__attribute__((naked)); ++ ++static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, ++ sigset_t *set, struct pt_regs *regs) ++{ ++ struct rt_sigframe *frame; ++ int err = 0; ++ ++ frame = (struct rt_sigframe *) get_sigframe(ka, regs, sizeof(*frame)); ++ ++ /* ++ * The 'err |=' have been may criticized as bad code style, but I ++ * strongly suspect that we want this code to be fast. So for ++ * now it stays as is. ++ */ ++ err |= __put_user( ( (current_thread_info()->exec_domain) ++ && (current_thread_info()->exec_domain->signal_invmap) ++ && (sig < 32) ) ++ ? current_thread_info()->exec_domain->signal_invmap[sig] ++ : sig, &frame->sig); ++ err |= __put_user(info, &frame->info); ++ ++ /* Create the ucontext. */ ++ err |= __put_user(0, &frame->uc.uc_flags); ++ err |= __put_user(0, &frame->uc.uc_link); ++ err |= __put_user((void *)current->sas_ss_sp, ++ &frame->uc.uc_stack.ss_sp); ++ err |= __put_user(sas_ss_flags(regs->an[7]), ++ &frame->uc.uc_stack.ss_flags); ++ err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); ++ err |= __put_user(__NR_rt_sigreturn, &frame->syscall_number); ++ if ((regs->frame_type == UBICOM32_FRAME_TYPE_TRAP) || ++ (regs->frame_type == UBICOM32_FRAME_TYPE_INTERRUPT)) { ++ err |= __put_user(1, &frame->restore_all_regs); ++ } else { ++ err |= __put_user(0, &frame->restore_all_regs); ++ } ++ err |= copy_to_user (&frame->uc.uc_mcontext.sc_regs, regs, sizeof(struct pt_regs)); ++ err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set)); ++ ++ if (err) ++ goto give_sigsegv; ++ ++ /* ++ * Set up registers for signal handler NOTE: Do not modify dn[14], it ++ * contains the userspace tls pointer, so it important that it carries ++ * over to the signal handler. ++ */ ++ regs->an[7] = (unsigned long)frame; ++ regs->pc = (unsigned long) signal_trampoline; ++ regs->an[5] = (unsigned long) signal_trampoline; ++ regs->dn[0] = sig; ++ regs->dn[1] = (unsigned long) frame->info; ++ regs->dn[2] = (unsigned int) &frame->uc; ++ ++ /* ++ * If this is FDPIC then the signal handler is actually a function ++ * descriptor. ++ */ ++ if (current->personality & FDPIC_FUNCPTRS) { ++ struct fdpic_func_descriptor __user *funcptr = ++ (struct fdpic_func_descriptor *) ka->sa.sa_handler; ++ err |= __get_user(regs->dn[3], &funcptr->text); ++ err |= __get_user(regs->an[0], &funcptr->GOT); ++ if (err) ++ goto give_sigsegv; ++ ++ /* ++ * The funcdesc must be in a3 as this is required for the lazy ++ * resolver in ld.so, if the application is not FDPIC a3 is not ++ * used. ++ */ ++ regs->an[3] = (unsigned long) funcptr; ++ ++ } else { ++ regs->dn[3] = (unsigned long)ka->sa.sa_handler; ++ regs->an[0] = 0; ++ } ++ ++ regs->frame_type = UBICOM32_FRAME_TYPE_SIGTRAMP; ++ ++ return; ++ ++give_sigsegv: ++ /* user space exception */ ++ force_sigsegv(sig, current); ++} ++ ++static inline void ++handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) ++{ ++ switch (regs->dn[0]) { ++ case -ERESTARTNOHAND: ++ if (!has_handler) ++ goto do_restart; ++ regs->dn[0] = -EINTR; ++ break; ++ ++ case -ERESTARTSYS: ++ if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { ++ regs->dn[0] = -EINTR; ++ break; ++ } ++ /* fallthrough */ ++ case -ERESTARTNOINTR: ++ do_restart: ++ regs->dn[0] = regs->original_dn_0; ++ regs->pc -= 8; ++ regs->an[5] -= 8; ++ break; ++ } ++} ++ ++/* ++ * OK, we're invoking a handler ++ */ ++static void ++handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, ++ sigset_t *oldset, struct pt_regs *regs) ++{ ++ /* are we from a system call? */ ++ if (regs->frame_type == -1) ++ /* If so, check system call restarting.. */ ++ handle_restart(regs, ka, 1); ++ ++ /* set up the stack frame */ ++ setup_rt_frame(sig, ka, info, oldset, regs); ++ ++ if (ka->sa.sa_flags & SA_ONESHOT) ++ ka->sa.sa_handler = SIG_DFL; ++ ++ spin_lock_irq(¤t->sighand->siglock); ++ sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); ++ if (!(ka->sa.sa_flags & SA_NODEFER)) ++ sigaddset(¤t->blocked,sig); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++} ++ ++/* ++ * Note that 'init' is a special process: it doesn't get signals it doesn't ++ * want to handle. Thus you cannot kill init even with a SIGKILL even by ++ * mistake. ++ */ ++asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) ++{ ++ struct k_sigaction ka; ++ siginfo_t info; ++ int signr; ++ ++ /* ++ * We want the common case to go fast, which ++ * is why we may in certain cases get here from ++ * kernel mode. Just return without doing anything ++ * if so. ++ */ ++ if (!user_mode(regs)) ++ return 1; ++ ++ if (!oldset) ++ oldset = ¤t->blocked; ++ ++ signr = get_signal_to_deliver(&info, &ka, regs, NULL); ++ if (signr > 0) { ++ /* Whee! Actually deliver the signal. */ ++ handle_signal(signr, &ka, &info, oldset, regs); ++ return 1; ++ } ++ ++ /* Did we come from a system call? */ ++ if (regs->frame_type == -1) { ++ /* Restart the system call - no handlers present */ ++ handle_restart(regs, NULL, 0); ++ } ++ ++ return 0; ++} ++ ++/* ++ * sys_sigreturn() ++ * Return handler for signal clean-up. ++ * ++ * NOTE: Ubicom32 does not use this syscall. Instead we rely ++ * on do_rt_sigreturn(). ++ */ ++asmlinkage long sys_sigreturn(void) ++{ ++ return -ENOSYS; ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/smp.c +@@ -0,0 +1,808 @@ ++/* ++ * arch/ubicom32/kernel/smp.c ++ * SMP implementation for Ubicom32 processors. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> ++ * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> ++ * Copyright (C) 2001,2004 Grant Grundler <grundler@parisc-linux.org> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/types.h> ++#include <linux/spinlock.h> ++#include <linux/slab.h> ++ ++#include <linux/kernel.h> ++#include <linux/bootmem.h> ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/smp.h> ++#include <linux/kernel_stat.h> ++#include <linux/mm.h> ++#include <linux/err.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/cpu.h> ++#include <linux/profile.h> ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/ptrace.h> ++#include <linux/unistd.h> ++#include <linux/irq.h> ++ ++#include <asm/system.h> ++#include <asm/atomic.h> ++#include <asm/current.h> ++#include <asm/tlbflush.h> ++#include <asm/timex.h> ++#include <asm/cpu.h> ++#include <asm/irq.h> ++#include <asm/processor.h> ++#include <asm/thread.h> ++#include <asm/sections.h> ++#include <asm/ip5000.h> ++ ++/* ++ * Mask the debug printout for IPI because they are too verbose ++ * for regular debugging. ++ */ ++ ++// #define DEBUG_SMP 1 ++#if !defined(DEBUG_SMP) ++#define smp_debug(lvl, ...) ++#else ++static unsigned int smp_debug_lvl = 50; ++#define smp_debug(lvl, printargs...) \ ++ if (lvl >= smp_debug_lvl) { \ ++ printk(printargs); \ ++ } ++#endif ++ ++#if !defined(DEBUG_SMP) ++#define DEBUG_ASSERT(cond) ++#else ++#define DEBUG_ASSERT(cond) \ ++ if (!(cond)) { \ ++ THREAD_STALL; \ ++ } ++#endif ++ ++/* ++ * List of IPI Commands (more than one can be set at a time). ++ */ ++enum ipi_message_type { ++ IPI_NOP, ++ IPI_RESCHEDULE, ++ IPI_CALL_FUNC, ++ IPI_CALL_FUNC_SINGLE, ++ IPI_CPU_STOP, ++ IPI_CPU_TIMER, ++}; ++ ++/* ++ * These values are properly adjusted by smp_prepare_cpus() below. They are ++ * required to be declared in the arch directory if CONFIG_SMP is set. ++ */ ++cpumask_t cpu_online_map = CPU_MASK_NONE; /* Bitmap of online CPUs */ ++EXPORT_SYMBOL(cpu_online_map); ++ ++cpumask_t cpu_possible_map = CPU_MASK_ALL; /* Bitmap of Present CPUs */ ++EXPORT_SYMBOL(cpu_possible_map); ++ ++/* ++ * We maintain a hardware thread oriented view of online threads ++ * and those involved or needing IPI. ++ */ ++static unsigned long smp_online_threads = 0; ++static unsigned long smp_needs_ipi = 0; ++static unsigned long smp_inside_ipi = 0; ++static unsigned long smp_irq_affinity[NR_IRQS]; ++ ++/* ++ * What do we need to track on a per cpu/thread basis? ++ */ ++DEFINE_PER_CPU(struct cpuinfo_ubicom32, cpu_data); ++ ++/* ++ * Each thread cpuinfo IPI information is guarded by a lock ++ * that is kept local to this file. ++ */ ++DEFINE_PER_CPU(spinlock_t, ipi_lock) = SPIN_LOCK_UNLOCKED; ++ ++/* ++ * The IPI(s) are based on a software IRQ through the LDSR. ++ */ ++unsigned int smp_ipi_irq; ++ ++/* ++ * Define a spinlock so that only one cpu is able to modify the ++ * smp_needs_ipi and to set/clear the IRQ at a time. ++ */ ++DEFINE_SPINLOCK(smp_ipi_lock); ++ ++/* ++ * smp_halt_processor() ++ * Halt this hardware thread. ++ */ ++static void smp_halt_processor(void) ++{ ++ int cpuid = thread_get_self(); ++ cpu_clear(smp_processor_id(), cpu_online_map); ++ local_irq_disable(); ++ printk(KERN_EMERG "cpu[%d] has halted. It is not OK to turn off power \ ++ until all cpu's are off.\n", cpuid); ++ for (;;) { ++ thread_suspend(); ++ } ++} ++ ++/* ++ * ipi_interrupt() ++ * Handle an Interprocessor Interrupt. ++ */ ++static irqreturn_t ipi_interrupt(int irq, void *dev_id) ++{ ++ int cpuid = smp_processor_id(); ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpuid); ++ unsigned long ops; ++ ++ /* ++ * Count this now; we may make a call that never returns. ++ */ ++ p->ipi_count++; ++ ++ /* ++ * We are about to process all ops. If another cpu has stated ++ * that we need an IPI, we will have already processed it. By ++ * clearing our smp_needs_ipi, and processing all ops, ++ * we reduce the number of IPI interrupts. However, this introduces ++ * the possibility that smp_needs_ipi will be clear and the soft irq ++ * will have gone off; so we need to make the get_affinity() path ++ * tolerant of spurious interrupts. ++ */ ++ spin_lock(&smp_ipi_lock); ++ smp_needs_ipi &= ~p->tid; ++ spin_unlock(&smp_ipi_lock); ++ ++ for (;;) { ++ /* ++ * Read the set of IPI commands we should handle. ++ */ ++ spinlock_t *lock = &per_cpu(ipi_lock, cpuid); ++ spin_lock(lock); ++ ops = p->ipi_pending; ++ p->ipi_pending = 0; ++ spin_unlock(lock); ++ ++ /* ++ * If we have no IPI commands to execute, break out. ++ */ ++ if (!ops) { ++ break; ++ } ++ ++ /* ++ * Execute the set of commands in the ops word, one command ++ * at a time in no particular order. Strip of each command ++ * as we execute it. ++ */ ++ while (ops) { ++ unsigned long which = ffz(~ops); ++ ops &= ~(1 << which); ++ ++ BUG_ON(!irqs_disabled()); ++ switch (which) { ++ case IPI_NOP: ++ smp_debug(100, KERN_INFO "cpu[%d]: " ++ "IPI_NOP\n", cpuid); ++ break; ++ ++ case IPI_RESCHEDULE: ++ /* ++ * Reschedule callback. Everything to be ++ * done is done by the interrupt return path. ++ */ ++ smp_debug(200, KERN_INFO "cpu[%d]: " ++ "IPI_RESCHEDULE\n", cpuid); ++ break; ++ ++ case IPI_CALL_FUNC: ++ smp_debug(100, KERN_INFO "cpu[%d]: " ++ "IPI_CALL_FUNC\n", cpuid); ++ generic_smp_call_function_interrupt(); ++ break; ++ ++ case IPI_CALL_FUNC_SINGLE: ++ smp_debug(100, KERN_INFO "cpu[%d]: " ++ "IPI_CALL_FUNC_SINGLE\n", cpuid); ++ generic_smp_call_function_single_interrupt(); ++ break; ++ ++ case IPI_CPU_STOP: ++ smp_debug(100, KERN_INFO "cpu[%d]: " ++ "IPI_CPU_STOP\n", cpuid); ++ smp_halt_processor(); ++ break; ++ ++#if !defined(CONFIG_LOCAL_TIMERS) ++ case IPI_CPU_TIMER: ++ smp_debug(100, KERN_INFO "cpu[%d]: " ++ "IPI_CPU_TIMER\n", cpuid); ++#if defined(CONFIG_GENERIC_CLOCKEVENTS) ++ local_timer_interrupt(); ++#else ++ update_process_times(user_mode(get_irq_regs())); ++ profile_tick(CPU_PROFILING); ++#endif ++#endif ++ break; ++ ++ default: ++ printk(KERN_CRIT "cpu[%d]: " ++ "Unknown IPI: %lu\n", cpuid, which); ++ ++ return IRQ_NONE; ++ } ++ ++ /* ++ * Let in any pending interrupts ++ */ ++ BUG_ON(!irqs_disabled()); ++ local_irq_enable(); ++ local_irq_disable(); ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++/* ++ * ipi_send() ++ * Send an Interprocessor Interrupt. ++ */ ++static void ipi_send(int cpu, enum ipi_message_type op) ++{ ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpu); ++ spinlock_t *lock = &per_cpu(ipi_lock, cpu); ++ unsigned long flags; ++ ++ /* ++ * We protect the setting of the ipi_pending field and ensure ++ * that the ipi delivery mechanism and interrupt are atomically ++ * handled. ++ */ ++ spin_lock_irqsave(lock, flags); ++ p->ipi_pending |= 1 << op; ++ spin_unlock_irqrestore(lock, flags); ++ ++ spin_lock_irqsave(&smp_ipi_lock, flags); ++ smp_needs_ipi |= (1 << p->tid); ++ ubicom32_set_interrupt(smp_ipi_irq); ++ spin_unlock_irqrestore(&smp_ipi_lock, flags); ++} ++ ++/* ++ * ipi_send_mask ++ * Send an IPI to each cpu in mask. ++ */ ++static inline void ipi_send_mask(unsigned int op, cpumask_t mask) ++{ ++ int cpu; ++ for_each_cpu_mask(cpu, mask) { ++ ipi_send(cpu, op); ++ } ++} ++ ++/* ++ * ipi_send_allbutself() ++ * Send an IPI to all threads but ourselves. ++ */ ++static inline void ipi_send_allbutself(unsigned int op) ++{ ++ int self = smp_processor_id(); ++ cpumask_t result = cpu_online_map; ++ cpu_clear(self, result); ++ ipi_send_mask(op, result); ++} ++ ++/* ++ * smp_enable_vector() ++ */ ++static void smp_enable_vector(unsigned int irq) ++{ ++ ubicom32_clear_interrupt(smp_ipi_irq); ++ ldsr_enable_vector(irq); ++} ++ ++/* ++ * smp_disable_vector() ++ * Disable the interrupt by clearing the appropriate bit in the ++ * LDSR Mask Register. ++ */ ++static void smp_disable_vector(unsigned int irq) ++{ ++ ldsr_disable_vector(irq); ++} ++ ++/* ++ * smp_mask_vector() ++ */ ++static void smp_mask_vector(unsigned int irq) ++{ ++ ldsr_mask_vector(irq); ++} ++ ++/* ++ * smp_unmask_vector() ++ */ ++static void smp_unmask_vector(unsigned int irq) ++{ ++ ldsr_unmask_vector(irq); ++} ++ ++/* ++ * smp_end_vector() ++ * Called once an interrupt is completed (reset the LDSR mask). ++ */ ++static void smp_end_vector(unsigned int irq) ++{ ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, smp_processor_id()); ++ spin_lock(&smp_ipi_lock); ++ smp_inside_ipi &= ~(1 << p->tid); ++ if (smp_inside_ipi) { ++ spin_unlock(&smp_ipi_lock); ++ return; ++ } ++ spin_unlock(&smp_ipi_lock); ++ ldsr_unmask_vector(irq); ++} ++ ++/* ++ * Special hanlder functions for SMP. ++ */ ++static struct irq_chip ubicom32_smp_chip = { ++ .name = "UbicoIPI", ++ .startup = NULL, ++ .shutdown = NULL, ++ .enable = smp_enable_vector, ++ .disable = smp_disable_vector, ++ .ack = NULL, ++ .mask = smp_mask_vector, ++ .unmask = smp_unmask_vector, ++ .end = smp_end_vector, ++}; ++ ++/* ++ * smp_reset_ipi() ++ * None of these cpu(s) got their IPI, turn it back on. ++ * ++ * Note: This is called by the LDSR which is not a full ++ * Linux cpu. Thus you must use the raw form of locks ++ * because lock debugging will not work on the partial ++ * cpu nature of the LDSR. ++ */ ++void smp_reset_ipi(unsigned long mask) ++{ ++ __raw_spin_lock(&smp_ipi_lock.raw_lock); ++ smp_needs_ipi |= mask; ++ smp_inside_ipi &= ~mask; ++ ubicom32_set_interrupt(smp_ipi_irq); ++ __raw_spin_unlock(&smp_ipi_lock.raw_lock); ++} ++ ++/* ++ * smp_get_affinity() ++ * Choose the thread affinity for this interrupt. ++ * ++ * Note: This is called by the LDSR which is not a full ++ * Linux cpu. Thus you must use the raw form of locks ++ * because lock debugging will not work on the partial ++ * cpu nature of the LDSR. ++ */ ++unsigned long smp_get_affinity(unsigned int irq, int *all) ++{ ++ unsigned long mask = 0; ++ ++ /* ++ * Most IRQ(s) are delivered in a round robin fashion. ++ */ ++ if (irq != smp_ipi_irq) { ++ unsigned long result = smp_irq_affinity[irq] & smp_online_threads; ++ DEBUG_ASSERT(result); ++ *all = 0; ++ return result; ++ } ++ ++ /* ++ * This is an IPI request. Return all cpu(s) scheduled for an IPI. ++ * We also track those cpu(s) that are going to be "receiving" IPI this ++ * round. When all CPU(s) have called smp_end_vector(), ++ * we will unmask the IPI interrupt. ++ */ ++ __raw_spin_lock(&smp_ipi_lock.raw_lock); ++ ubicom32_clear_interrupt(smp_ipi_irq); ++ if (smp_needs_ipi) { ++ mask = smp_needs_ipi; ++ smp_inside_ipi |= smp_needs_ipi; ++ smp_needs_ipi = 0; ++ } ++ __raw_spin_unlock(&smp_ipi_lock.raw_lock); ++ *all = 1; ++ return mask; ++} ++ ++/* ++ * smp_set_affinity() ++ * Set the affinity for this irq but store the value in tid(s). ++ */ ++void smp_set_affinity(unsigned int irq, cpumask_t dest) ++{ ++ int cpuid; ++ unsigned long *paffinity = &smp_irq_affinity[irq]; ++ ++ /* ++ * If none specified, all cpus are allowed. ++ */ ++ if (cpus_empty(dest)) { ++ *paffinity = 0xffffffff; ++ return; ++ } ++ ++ /* ++ * Make sure to clear the old value before setting up the ++ * list. ++ */ ++ *paffinity = 0; ++ for_each_cpu_mask(cpuid, dest) { ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpuid); ++ *paffinity |= (1 << p->tid); ++ } ++} ++ ++/* ++ * smp_send_stop() ++ * Send a stop request to all CPU but this one. ++ */ ++void smp_send_stop(void) ++{ ++ ipi_send_allbutself(IPI_CPU_STOP); ++} ++ ++/* ++ * smp_send_timer_all() ++ * Send all cpu(s) but this one, a request to update times. ++ */ ++void smp_send_timer_all(void) ++{ ++ ipi_send_allbutself(IPI_CPU_TIMER); ++} ++ ++/* ++ * smp_timer_broadcast() ++ * Use an IPI to broadcast a timer message ++ */ ++void smp_timer_broadcast(cpumask_t mask) ++{ ++ ipi_send_mask(IPI_CPU_TIMER, mask); ++} ++ ++/* ++ * smp_send_reschedule() ++ * Send a reschedule request to the specified cpu. ++ */ ++void smp_send_reschedule(int cpu) ++{ ++ ipi_send(cpu, IPI_RESCHEDULE); ++} ++ ++/* ++ * arch_send_call_function_ipi() ++ * Cause each cpu in the mask to call the generic function handler. ++ */ ++void arch_send_call_function_ipi(cpumask_t mask) ++{ ++ int cpu; ++ for_each_cpu_mask(cpu, mask) { ++ ipi_send(cpu, IPI_CALL_FUNC); ++ } ++} ++ ++/* ++ * arch_send_call_function_single_ipi() ++ * Cause the specified cpu to call the generic function handler. ++ */ ++void arch_send_call_function_single_ipi(int cpu) ++{ ++ ipi_send(cpu, IPI_CALL_FUNC_SINGLE); ++} ++ ++/* ++ * smp_mainline_start() ++ * Start a slave thread executing a mainline Linux context. ++ */ ++static void __init smp_mainline_start(void *arg) ++{ ++ int cpuid = smp_processor_id(); ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpuid); ++ ++ BUG_ON(p->tid != thread_get_self()); ++ ++ /* ++ * Well, support 2.4 linux scheme as well. ++ */ ++ if (cpu_test_and_set(cpuid, cpu_online_map)) { ++ printk(KERN_CRIT "cpu[%d]: already initialized!\n", cpuid); ++ smp_halt_processor(); ++ return; ++ } ++ ++ /* ++ * Initialise the idle task for this CPU ++ */ ++ atomic_inc(&init_mm.mm_count); ++ current->active_mm = &init_mm; ++ if (current->mm) { ++ printk(KERN_CRIT "cpu[%d]: idle task already has memory " ++ "management\n", cpuid); ++ smp_halt_processor(); ++ return; ++ } ++ ++ /* ++ * TODO: X86 does this prior to calling notify, try to understand why? ++ */ ++ preempt_disable(); ++ ++#if defined(CONFIG_GENERIC_CLOCKEVENTS) ++ /* ++ * Setup a local timer event so that this cpu will get timer interrupts ++ */ ++ if (local_timer_setup(cpuid) == -1) { ++ printk(KERN_CRIT "cpu[%d]: timer alloc failed\n", cpuid); ++ smp_halt_processor(); ++ return; ++ } ++#endif ++ ++ /* ++ * Notify those interested that we are up and alive. This must ++ * be done before interrupts are enabled. It must also be completed ++ * before the bootstrap cpu returns from __cpu_up() (see comment ++ * above cpu_set() of the cpu_online_map). ++ */ ++ notify_cpu_starting(cpuid); ++ ++ /* ++ * Indicate that this thread is now online and present. Setting ++ * cpu_online_map has the side effect of allowing the bootstrap ++ * cpu to continue along; so anything that MUST be done prior to the ++ * bootstrap cpu returning from __cpu_up() needs to go above here. ++ */ ++ cpu_set(cpuid, cpu_online_map); ++ cpu_set(cpuid, cpu_present_map); ++ ++ /* ++ * Maintain a thread mapping in addition to the cpu mapping. ++ */ ++ smp_online_threads |= (1 << p->tid); ++ ++ /* ++ * Enable interrupts for this thread. ++ */ ++ local_irq_enable(); ++ ++ /* ++ * Enter the idle loop and wait for a timer to schedule some work. ++ */ ++ printk(KERN_INFO "cpu[%d]: entering cpu_idle()\n", cpuid); ++ cpu_idle(); ++ ++ /* Not Reached */ ++} ++ ++/* ++ * smp_cpus_done() ++ * Called once the kernel_init() has brought up all cpu(s). ++ */ ++void smp_cpus_done(unsigned int cpu_max) ++{ ++ /* Do Nothing */ ++} ++ ++/* ++ * __cpu_up() ++ * Called to startup a sepcific cpu. ++ */ ++int __cpuinit __cpu_up(unsigned int cpu) ++{ ++ struct task_struct *idle; ++ unsigned int *stack; ++ long timeout; ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, cpu); ++ ++ /* ++ * Create an idle task for this CPU. ++ */ ++ idle = fork_idle(cpu); ++ if (IS_ERR(idle)) { ++ panic("cpu[%d]: fork failed\n", cpu); ++ return -ENOSYS; ++ } ++ task_thread_info(idle)->cpu = cpu; ++ ++ /* ++ * Setup the sw_ksp[] to point to this new task. ++ */ ++ sw_ksp[p->tid] = (unsigned int)idle->stack; ++ stack = (unsigned int *)(sw_ksp[p->tid] + PAGE_SIZE - 8); ++ ++ /* ++ * Cause the specified thread to execute our smp_mainline_start ++ * function as a TYPE_NORMAL thread. ++ */ ++ printk(KERN_INFO "cpu[%d]: launching mainline Linux thread\n", cpu); ++ if (thread_start(p->tid, smp_mainline_start, (void *)NULL, stack, ++ THREAD_TYPE_NORMAL) == -1) { ++ printk(KERN_WARNING "cpu[%d]: failed thread_start\n", cpu); ++ return -ENOSYS; ++ } ++ ++ /* ++ * Wait for the thread to start up. The thread will set ++ * the online bit when it is running. Our caller execpts the ++ * cpu to be online if we return 0. ++ */ ++ for (timeout = 0; timeout < 10000; timeout++) { ++ if (cpu_online(cpu)) { ++ break; ++ } ++ ++ udelay(100); ++ barrier(); ++ continue; ++ } ++ ++ if (!cpu_online(cpu)) { ++ printk(KERN_CRIT "cpu[%d]: failed to live after %ld us\n", ++ cpu, timeout * 100); ++ return -ENOSYS; ++ } ++ ++ printk(KERN_INFO "cpu[%d]: came alive after %ld us\n", ++ cpu, timeout * 100); ++ return 0; ++} ++ ++/* ++ * Data used by setup_irq for the IPI. ++ */ ++static struct irqaction ipi_irq = { ++ .name = "ipi", ++ .flags = IRQF_DISABLED | IRQF_PERCPU, ++ .handler = ipi_interrupt, ++}; ++ ++/* ++ * smp_prepare_cpus() ++ * Mark threads that are available to Linux as possible cpus(s). ++ */ ++void __init smp_prepare_cpus(unsigned int max_cpus) ++{ ++ int i; ++ ++ /* ++ * We will need a software IRQ to send IPI(s). We will use ++ * a single software IRQ for all IPI(s). ++ */ ++ if (irq_soft_alloc(&smp_ipi_irq) < 0) { ++ panic("no software IRQ is available\n"); ++ return; ++ } ++ ++ /* ++ * For the IPI interrupt, we want to use our own chip definition. ++ * This allows us to define what happens in SMP IPI without affecting ++ * the performance of the other interrupts. ++ * ++ * Next, Register the IPI interrupt function against the soft IRQ. ++ */ ++ set_irq_chip(smp_ipi_irq, &ubicom32_smp_chip); ++ setup_irq(smp_ipi_irq, &ipi_irq); ++ ++ /* ++ * We use the device tree node to determine how many ++ * free cpus we will have (up to NR_CPUS) and we indicate ++ * that those cpus are present. ++ * ++ * We need to do this very early in the SMP case ++ * because the Linux init code uses the cpu_present_map. ++ */ ++ for_each_possible_cpu(i) { ++ thread_t tid; ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, i); ++ ++ /* ++ * Skip the bootstrap cpu ++ */ ++ if (i == 0) { ++ continue; ++ } ++ ++ /* ++ * If we have a free thread left in the mask, ++ * indicate that the cpu is present. ++ */ ++ tid = thread_alloc(); ++ if (tid == (thread_t)-1) { ++ break; ++ } ++ ++ /* ++ * Save the hardware thread id for this cpu. ++ */ ++ p->tid = tid; ++ cpu_set(i, cpu_present_map); ++ printk(KERN_INFO "cpu[%d]: added to cpu_present_map - tid: %d\n", i, tid); ++ } ++} ++ ++/* ++ * smp_prepare_boot_cpu() ++ * Copy the per_cpu data into the appropriate spot for the bootstrap cpu. ++ * ++ * The code in boot_cpu_init() has already set the boot cpu's ++ * state in the possible, present, and online maps. ++ */ ++void __devinit smp_prepare_boot_cpu(void) ++{ ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, 0); ++ ++ smp_online_threads |= (1 << p->tid); ++ printk(KERN_INFO "cpu[%d]: bootstrap CPU online - tid: %ld\n", ++ current_thread_info()->cpu, p->tid); ++} ++ ++/* ++ * smp_setup_processor_id() ++ * Set the current_thread_info() structure cpu value. ++ * ++ * We set the value to the true hardware thread value that we are running on. ++ * NOTE: this function overrides the weak alias function in main.c ++ */ ++void __init smp_setup_processor_id(void) ++{ ++ struct cpuinfo_ubicom32 *p = &per_cpu(cpu_data, 0); ++ current_thread_info()->cpu = 0; ++ p->tid = thread_get_self(); ++} ++ ++/* ++ * setup_profiling_timer() ++ * Dummy function created to keep Oprofile happy in the SMP case. ++ */ ++int setup_profiling_timer(unsigned int multiplier) ++{ ++ return 0; ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/stacktrace.c +@@ -0,0 +1,243 @@ ++/* ++ * arch/ubicom32/kernel/stacktrace.c ++ * Ubicom32 architecture stack back trace implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/sched.h> ++#include <linux/stacktrace.h> ++#include <linux/module.h> ++#include <asm/stacktrace.h> ++#include <asm/thread.h> ++ ++/* ++ * These symbols are filled in by the linker. ++ */ ++extern unsigned long _stext; ++extern unsigned long _etext; ++ ++extern unsigned long __ocm_text_run_begin; ++extern unsigned long __data_begin; ++ ++/* ++ * stacktrace_iterate() ++ * Walk the stack looking for call and calli instructions on an aligned ++ * boundary. ++ * ++ * Trace must point to the top of the current stack frame. ++ */ ++unsigned long stacktrace_iterate(unsigned long **trace, ++ unsigned long stext, ++ unsigned long etext, ++ unsigned long ocm_stext, ++ unsigned long ocm_etext, ++ unsigned long sstack, ++ unsigned long estack) ++{ ++ unsigned int thread_trap_en, instruction; ++ unsigned long address; ++ unsigned int limit = 0; ++ unsigned long result = 0; ++ unsigned long *sp = *trace; ++ ++ /* ++ * Exclude the current thread from being monitored for traps. ++ */ ++ asm volatile( ++ " thread_get_self_mask d15 \n\t" ++ /* save current trap status */ ++ " and.4 %0, MT_TRAP_EN, d15 \n\t" ++ " not.4 d15, d15 \n\t" ++ /* disable trap */ ++ " and.4 MT_TRAP_EN, MT_TRAP_EN, d15 \n\t" ++ " pipe_flush 0 \n\t" ++ : "=r" (thread_trap_en) ++ : ++ : "d15", "cc" ++ ); ++ ++ while (limit++ < 256) { ++ /* ++ * See if we have a valid stack. ++ */ ++ if (!between((unsigned long)sp, sstack, estack)) { ++#ifdef TRAP_DEBUG_STACK_TRACE ++ printk(KERN_EMERG "stack address is out of range - " ++ "sp: %x, sstack: %x, estack: %x\n", ++ (unsigned int)sp, (unsigned int)sstack, ++ (unsigned int)estack); ++#endif ++ result = 0; ++ *trace = 0; ++ break; ++ } ++ ++ /* ++ * Get the value off the stack and back up 4 bytes to what ++ * should be the address of a call or calli. ++ */ ++ address = (*sp++) - 4; ++ ++ /* ++ * If the address is not within the text segment, skip this ++ * value. ++ */ ++ if (!between(address, stext, etext) && ++ !between(address, ocm_stext, ocm_etext)) { ++#ifdef TRAP_DEBUG_STACK_TRACE ++ printk(KERN_EMERG "not a text address - " ++ "address: %08x, stext: %08x, etext: %08x\n" ++ "ocm_stext: %08x, ocm_etext: %08x\n", ++ (unsigned int)address, ++ (unsigned int)stext, ++ (unsigned int)etext, ++ (unsigned int)ocm_stext, ++ (unsigned int)ocm_etext); ++#endif ++ continue; ++ ++ } ++ ++ /* ++ * If the address is not on an aligned boundary it can not be a ++ * return address. ++ */ ++ if (address & 0x3) { ++ continue; ++ } ++ ++ /* ++ * Read the probable instruction. ++ */ ++ instruction = *(unsigned int *)address; ++ ++ /* ++ * Is this a call instruction? ++ */ ++ if ((instruction & 0xF8000000) == (u32_t)(0x1B << 27)) { ++#ifdef TRAP_DEBUG_STACK_TRACE ++ printk(KERN_EMERG "call inst. result: %x, " ++ "test: %x\n", (unsigned int)address, ++ (unsigned int)instruction); ++#endif ++ *trace = sp; ++ result = address; ++ break; ++ } ++ ++ /* ++ * Is this a calli instruction? ++ */ ++ if ((instruction & 0xF8000000) == (u32_t)(0x1E << 27)) { ++#ifdef TRAP_DEBUG_STACK_TRACE ++ printk(KERN_EMERG "calli inst. result: %x, " ++ "test: %x\n", (unsigned int)address, ++ (unsigned int)instruction); ++#endif ++ *trace = sp; ++ result = address; ++ break; ++ } ++ } ++ ++ /* ++ * Restore the current thread to be monitored for traps. ++ */ ++ if (thread_trap_en) { ++ asm volatile( ++ " thread_get_self_mask d15 \n\t" ++ " or.4 MT_TRAP_EN, MT_TRAP_EN, d15 \n\t" ++ : ++ : ++ : "d15", "cc" ++ ); ++ } ++ return result; ++} ++ ++#ifdef CONFIG_STACKTRACE ++/* ++ * stacktrace_save_entries() ++ * Save stack back trace information into the provided trace structure. ++ */ ++void stacktrace_save_entries(struct task_struct *tsk, ++ struct stack_trace *trace, ++ unsigned long sp) ++{ ++ unsigned long code_start = (unsigned long)&_stext; ++ unsigned long code_end = (unsigned long)&_etext; ++ unsigned long ocm_code_start = (unsigned long)&__ocm_text_run_begin; ++ unsigned long ocm_code_end = (unsigned long)&__data_begin; ++ unsigned long stack_end = (unsigned long)(tsk->stack + THREAD_SIZE - 8); ++ unsigned long stack = (unsigned long)sp; ++ unsigned int idx = 0; ++ unsigned long *handle; ++ int skip = trace->skip; ++ ++ handle = (unsigned long *)stack; ++ while (idx < trace->max_entries) { ++ if (skip) { ++ skip--; ++ continue; ++ } ++ trace->entries[idx] = stacktrace_iterate(&handle, ++ code_start, code_end, ++ ocm_code_start, ocm_code_end, ++ (unsigned long)stack, stack_end); ++ if (trace->entries[idx] == 0) { ++ break; ++ } ++ idx++; ++ } ++} ++ ++/* ++ * save_stack_trace() ++ * Save the specified amount of the kernel stack trace information ++ * for the current task. ++ */ ++void save_stack_trace(struct stack_trace *trace) ++{ ++ unsigned long sp = 0; ++ asm volatile ( ++ " move.4 %0, SP \n\t" ++ : "=r" (sp) ++ ); ++ stacktrace_save_entries(current, trace, sp); ++} ++EXPORT_SYMBOL_GPL(save_stack_trace); ++ ++/* ++ * save_stack_trace_tsk() ++ * Save the specified amount of the kernel stack trace information ++ * for the specified task. ++ * ++ * Note: We assume the specified task is not currently running. ++ */ ++void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) ++{ ++ stacktrace_save_entries(tsk, trace, tsk->thread.sp); ++} ++EXPORT_SYMBOL_GPL(save_stack_trace_tsk); ++#endif /* CONFIG_STACKTRACE */ +--- /dev/null ++++ b/arch/ubicom32/kernel/syscalltable.S +@@ -0,0 +1,377 @@ ++/* ++ * arch/ubicom32/kernel/syscalltable.S ++ * <TODO: Replace with short file description> ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++/* ++ * ++ * Copyright (C) 2002, Greg Ungerer (gerg@snapgear.com) ++ * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, Kenneth Albanowski <kjahds@kjahds.com>, ++ * Copyright (C) 2000 Lineo Inc. (www.lineo.com) ++ * Copyright (C) 1991, 1992 Linus Torvalds ++ */ ++ ++#include <linux/sys.h> ++#include <linux/linkage.h> ++#include <asm/unistd.h> ++ ++.text ++ALIGN ++ .global sys_call_table ++sys_call_table: ++ .long sys_ni_syscall /* 0 - old "setup()" system call*/ ++ .long sys_exit ++ .long sys_fork ++ .long sys_read ++ .long sys_write ++ .long sys_open /* 5 */ ++ .long sys_close ++ .long sys_waitpid ++ .long sys_creat ++ .long sys_link ++ .long sys_unlink /* 10 */ ++ .long execve_intercept ++ .long sys_chdir ++ .long sys_time ++ .long sys_mknod ++ .long sys_chmod /* 15 */ ++ .long sys_chown16 ++ .long sys_ni_syscall /* old break syscall holder */ ++ .long sys_stat ++ .long sys_lseek ++ .long sys_getpid /* 20 */ ++ .long sys_mount ++ .long sys_oldumount ++ .long sys_setuid16 ++ .long sys_getuid16 ++ .long sys_stime /* 25 */ ++ .long sys_ptrace ++ .long sys_alarm ++ .long sys_fstat ++ .long sys_pause ++ .long sys_utime /* 30 */ ++ .long sys_ni_syscall /* old stty syscall holder */ ++ .long sys_ni_syscall /* old gtty syscall holder */ ++ .long sys_access ++ .long sys_nice ++ .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ ++ .long sys_sync ++ .long sys_kill ++ .long sys_rename ++ .long sys_mkdir ++ .long sys_rmdir /* 40 */ ++ .long sys_dup ++ .long sys_pipe ++ .long sys_times ++ .long sys_ni_syscall /* old prof syscall holder */ ++ .long sys_brk /* 45 */ ++ .long sys_setgid16 ++ .long sys_getgid16 ++ .long sys_signal ++ .long sys_geteuid16 ++ .long sys_getegid16 /* 50 */ ++ .long sys_acct ++ .long sys_umount /* recycled never used phys() */ ++ .long sys_ni_syscall /* old lock syscall holder */ ++ .long sys_ioctl ++ .long sys_fcntl /* 55 */ ++ .long sys_ni_syscall /* old mpx syscall holder */ ++ .long sys_setpgid ++ .long sys_ni_syscall /* old ulimit syscall holder */ ++ .long sys_ni_syscall ++ .long sys_umask /* 60 */ ++ .long sys_chroot ++ .long sys_ustat ++ .long sys_dup2 ++ .long sys_getppid ++ .long sys_getpgrp /* 65 */ ++ .long sys_setsid ++ .long sys_sigaction ++ .long sys_sgetmask ++ .long sys_ssetmask ++ .long sys_setreuid16 /* 70 */ ++ .long sys_setregid16 ++ .long sys_sigsuspend ++ .long sys_sigpending ++ .long sys_sethostname ++ .long sys_setrlimit /* 75 */ ++ .long sys_old_getrlimit ++ .long sys_getrusage ++ .long sys_gettimeofday ++ .long sys_settimeofday ++ .long sys_getgroups16 /* 80 */ ++ .long sys_setgroups16 ++ .long old_select ++ .long sys_symlink ++ .long sys_lstat ++ .long sys_readlink /* 85 */ ++ .long sys_uselib ++ .long sys_ni_syscall /* _sys_swapon */ ++ .long sys_reboot ++ .long sys_old_readdir ++ .long old_mmap /* 90 */ ++ .long sys_munmap ++ .long sys_truncate ++ .long sys_ftruncate ++ .long sys_fchmod ++ .long sys_fchown16 /* 95 */ ++ .long sys_getpriority ++ .long sys_setpriority ++ .long sys_ni_syscall /* old profil syscall holder */ ++ .long sys_statfs ++ .long sys_fstatfs /* 100 */ ++ .long sys_ni_syscall /* ioperm for i386 */ ++ .long sys_socketcall ++ .long sys_syslog ++ .long sys_setitimer ++ .long sys_getitimer /* 105 */ ++ .long sys_newstat ++ .long sys_newlstat ++ .long sys_newfstat ++ .long sys_ni_syscall ++ .long sys_ni_syscall /* iopl for i386 */ /* 110 */ ++ .long sys_vhangup ++ .long sys_ni_syscall /* obsolete idle() syscall */ ++ .long sys_ni_syscall /* vm86old for i386 */ ++ .long sys_wait4 ++ .long sys_ni_syscall /* 115 */ /* _sys_swapoff */ ++ .long sys_sysinfo ++ .long sys_ipc ++ .long sys_fsync ++ .long sys_sigreturn ++ .long clone_intercept /* 120 */ ++ .long sys_setdomainname ++ .long sys_newuname ++ .long sys_cacheflush /* modify_ldt for i386 */ ++ .long sys_adjtimex ++ .long sys_ni_syscall /* 125 */ /* _sys_mprotect */ ++ .long sys_sigprocmask ++ .long sys_ni_syscall /* old "creat_module" */ ++ .long sys_init_module ++ .long sys_delete_module ++ .long sys_ni_syscall /* 130: old "get_kernel_syms" */ ++ .long sys_quotactl ++ .long sys_getpgid ++ .long sys_fchdir ++ .long sys_bdflush ++ .long sys_sysfs /* 135 */ ++ .long sys_personality ++ .long sys_ni_syscall /* for afs_syscall */ ++ .long sys_setfsuid16 ++ .long sys_setfsgid16 ++ .long sys_llseek /* 140 */ ++ .long sys_getdents ++ .long sys_select ++ .long sys_flock ++ .long sys_ni_syscall /* _sys_msync */ ++ .long sys_readv /* 145 */ ++ .long sys_writev ++ .long sys_getsid ++ .long sys_fdatasync ++ .long sys_sysctl ++ .long sys_ni_syscall /* 150 */ /* _sys_mlock */ ++ .long sys_ni_syscall /* _sys_munlock */ ++ .long sys_ni_syscall /* _sys_mlockall */ ++ .long sys_ni_syscall /* _sys_munlockall */ ++ .long sys_sched_setparam ++ .long sys_sched_getparam /* 155 */ ++ .long sys_sched_setscheduler ++ .long sys_sched_getscheduler ++ .long sys_sched_yield ++ .long sys_sched_get_priority_max ++ .long sys_sched_get_priority_min /* 160 */ ++ .long sys_sched_rr_get_interval ++ .long sys_nanosleep ++ .long sys_ni_syscall /* _sys_mremap */ ++ .long sys_setresuid16 ++ .long sys_getresuid16 /* 165 */ ++ .long sys_getpagesize /* _sys_getpagesize */ ++ .long sys_ni_syscall /* old "query_module" */ ++ .long sys_poll ++ .long sys_ni_syscall /* _sys_nfsservctl */ ++ .long sys_setresgid16 /* 170 */ ++ .long sys_getresgid16 ++ .long sys_prctl ++ .long sys_rt_sigreturn ++ .long sys_rt_sigaction ++ .long sys_rt_sigprocmask /* 175 */ ++ .long sys_rt_sigpending ++ .long sys_rt_sigtimedwait ++ .long sys_rt_sigqueueinfo ++ .long sys_rt_sigsuspend ++ .long sys_pread64 /* 180 */ ++ .long sys_pwrite64 ++ .long sys_lchown16 ++ .long sys_getcwd ++ .long sys_capget ++ .long sys_capset /* 185 */ ++ .long sys_sigaltstack ++ .long sys_sendfile ++ .long sys_ni_syscall /* streams1 */ ++ .long sys_ni_syscall /* streams2 */ ++ .long vfork_intercept /* 190 */ ++ .long sys_getrlimit ++ .long sys_mmap2 ++ .long sys_truncate64 ++ .long sys_ftruncate64 ++ .long sys_stat64 /* 195 */ ++ .long sys_lstat64 ++ .long sys_fstat64 ++ .long sys_chown ++ .long sys_getuid ++ .long sys_getgid /* 200 */ ++ .long sys_geteuid ++ .long sys_getegid ++ .long sys_setreuid ++ .long sys_setregid ++ .long sys_getgroups /* 205 */ ++ .long sys_setgroups ++ .long sys_fchown ++ .long sys_setresuid ++ .long sys_getresuid ++ .long sys_setresgid /* 210 */ ++ .long sys_getresgid ++ .long sys_lchown ++ .long sys_setuid ++ .long sys_setgid ++ .long sys_setfsuid /* 215 */ ++ .long sys_setfsgid ++ .long sys_pivot_root ++ .long sys_ni_syscall ++ .long sys_ni_syscall ++ .long sys_getdents64 /* 220 */ ++ .long sys_gettid ++ .long sys_tkill ++ .long sys_setxattr ++ .long sys_lsetxattr ++ .long sys_fsetxattr /* 225 */ ++ .long sys_getxattr ++ .long sys_lgetxattr ++ .long sys_fgetxattr ++ .long sys_listxattr ++ .long sys_llistxattr /* 230 */ ++ .long sys_flistxattr ++ .long sys_removexattr ++ .long sys_lremovexattr ++ .long sys_fremovexattr ++ .long sys_futex /* 235 */ ++ .long sys_sendfile64 ++ .long sys_ni_syscall /* _sys_mincore */ ++ .long sys_ni_syscall /* _sys_madvise */ ++ .long sys_fcntl64 ++ .long sys_readahead /* 240 */ ++ .long sys_io_setup ++ .long sys_io_destroy ++ .long sys_io_getevents ++ .long sys_io_submit ++ .long sys_io_cancel /* 245 */ ++ .long sys_fadvise64 ++ .long sys_exit_group ++ .long sys_lookup_dcookie ++ .long sys_epoll_create ++ .long sys_epoll_ctl /* 250 */ ++ .long sys_epoll_wait ++ .long sys_ni_syscall /* _sys_remap_file_pages */ ++ .long sys_set_tid_address ++ .long sys_timer_create ++ .long sys_timer_settime /* 255 */ ++ .long sys_timer_gettime ++ .long sys_timer_getoverrun ++ .long sys_timer_delete ++ .long sys_clock_settime ++ .long sys_clock_gettime /* 260 */ ++ .long sys_clock_getres ++ .long sys_clock_nanosleep ++ .long sys_statfs64 ++ .long sys_fstatfs64 ++ .long sys_tgkill /* 265 */ ++ .long sys_utimes ++ .long sys_fadvise64_64 ++ .long sys_mbind ++ .long sys_get_mempolicy ++ .long sys_set_mempolicy /* 270 */ ++ .long sys_mq_open ++ .long sys_mq_unlink ++ .long sys_mq_timedsend ++ .long sys_mq_timedreceive ++ .long sys_mq_notify /* 275 */ ++ .long sys_mq_getsetattr ++ .long sys_waitid ++ .long sys_ni_syscall /* for _sys_vserver */ ++ .long sys_add_key ++ .long sys_request_key /* 280 */ ++ .long sys_keyctl ++ .long sys_ioprio_set ++ .long sys_ioprio_get ++ .long sys_inotify_init ++ .long sys_inotify_add_watch /* 285 */ ++ .long sys_inotify_rm_watch ++ .long sys_migrate_pages ++ .long sys_openat ++ .long sys_mkdirat ++ .long sys_mknodat /* 290 */ ++ .long sys_fchownat ++ .long sys_futimesat ++ .long sys_fstatat64 ++ .long sys_unlinkat ++ .long sys_renameat /* 295 */ ++ .long sys_linkat ++ .long sys_symlinkat ++ .long sys_readlinkat ++ .long sys_fchmodat ++ .long sys_faccessat /* 300 */ ++ .long sys_ni_syscall /* Reserved for pselect6 */ ++ .long sys_ni_syscall /* Reserved for ppoll */ ++ .long sys_unshare ++ .long sys_set_robust_list ++ .long sys_get_robust_list /* 305 */ ++ .long sys_splice ++ .long sys_sync_file_range ++ .long sys_tee ++ .long sys_vmsplice ++ .long sys_move_pages /* 310 */ ++ .long sys_sched_setaffinity ++ .long sys_sched_getaffinity ++ .long sys_kexec_load ++ .long sys_getcpu ++ .long sys_epoll_pwait /* 315 */ ++ .long sys_utimensat ++ .long sys_signalfd ++ .long sys_timerfd_create ++ .long sys_eventfd ++ .long sys_fallocate /* 320 */ ++ .long sys_timerfd_settime ++ .long sys_timerfd_gettime ++ .long sys_ni_syscall /* sys_signalfd4 */ ++ .long sys_ni_syscall /* sys_eventfd2 */ ++ .long sys_ni_syscall /* sys_epoll_create1 */ ++ /* 325 */ ++ .long sys_ni_syscall /* sys_dup3 */ ++ .long sys_ni_syscall /* sys_pipe2 */ ++ .long sys_ni_syscall /* sys_inotify_init1 */ ++ .rept NR_syscalls-(.-sys_call_table)/4 ++ .long sys_ni_syscall ++ .endr ++ +--- /dev/null ++++ b/arch/ubicom32/kernel/sys_ubicom32.c +@@ -0,0 +1,237 @@ ++/* ++ * arch/ubicom32/kernel/sys_ubicom32.c ++ * Ubicom32 architecture system call support implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * ++ * This file contains various random system calls that ++ * have a non-standard calling sequence on the Linux/ubicom32 ++ * platform. ++ */ ++ ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/sched.h> ++#include <linux/mm.h> ++#include <linux/smp.h> ++#include <linux/sem.h> ++#include <linux/msg.h> ++#include <linux/shm.h> ++#include <linux/stat.h> ++#include <linux/syscalls.h> ++#include <linux/mman.h> ++#include <linux/file.h> ++#include <linux/utsname.h> ++#include <linux/ipc.h> ++#include <linux/fs.h> ++#include <linux/uaccess.h> ++#include <linux/unistd.h> ++ ++#include <asm/setup.h> ++#include <asm/traps.h> ++#include <asm/cacheflush.h> ++ ++/* common code for old and new mmaps */ ++static inline long do_mmap2( ++ unsigned long addr, unsigned long len, ++ unsigned long prot, unsigned long flags, ++ unsigned long fd, unsigned long pgoff) ++{ ++ int error = -EBADF; ++ struct file *file = NULL; ++ ++ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); ++ if (!(flags & MAP_ANONYMOUS)) { ++ file = fget(fd); ++ if (!file) ++ goto out; ++ } ++ ++ down_write(¤t->mm->mmap_sem); ++ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); ++ up_write(¤t->mm->mmap_sem); ++ ++ if (file) ++ fput(file); ++out: ++ return error; ++} ++ ++asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, ++ unsigned long prot, unsigned long flags, ++ unsigned long fd, unsigned long pgoff) ++{ ++ return do_mmap2(addr, len, prot, flags, fd, pgoff); ++} ++ ++/* ++ * Perform the select(nd, in, out, ex, tv) and mmap() system ++ * calls. Linux/m68k cloned Linux/i386, which didn't use to be able to ++ * handle more than 4 system call parameters, so these system calls ++ * used a memory block for parameter passing.. ++ */ ++ ++struct mmap_arg_struct { ++ unsigned long addr; ++ unsigned long len; ++ unsigned long prot; ++ unsigned long flags; ++ unsigned long fd; ++ unsigned long offset; ++}; ++ ++asmlinkage int old_mmap(struct mmap_arg_struct *arg) ++{ ++ struct mmap_arg_struct a; ++ int error = -EFAULT; ++ ++ if (copy_from_user(&a, arg, sizeof(a))) ++ goto out; ++ ++ error = -EINVAL; ++ if (a.offset & ~PAGE_MASK) ++ goto out; ++ ++ a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); ++ ++ error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, ++ a.offset >> PAGE_SHIFT); ++out: ++ return error; ++} ++ ++struct sel_arg_struct { ++ unsigned long n; ++ fd_set *inp, *outp, *exp; ++ struct timeval *tvp; ++}; ++ ++asmlinkage int old_select(struct sel_arg_struct *arg) ++{ ++ struct sel_arg_struct a; ++ ++ if (copy_from_user(&a, arg, sizeof(a))) ++ return -EFAULT; ++ /* sys_select() does the appropriate kernel locking */ ++ return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); ++} ++ ++/* ++ * sys_ipc() is the de-multiplexer for the SysV IPC calls.. ++ * ++ * This is really horribly ugly. ++ */ ++asmlinkage int sys_ipc(uint call, int first, int second, ++ int third, void *ptr, long fifth) ++{ ++ int version, ret; ++ ++ version = call >> 16; /* hack for backward compatibility */ ++ call &= 0xffff; ++ ++ if (call <= SEMCTL) ++ switch (call) { ++ case SEMOP: ++ return sys_semop(first, (struct sembuf *)ptr, second); ++ case SEMGET: ++ return sys_semget(first, second, third); ++ case SEMCTL: { ++ union semun fourth; ++ if (!ptr) ++ return -EINVAL; ++ if (get_user(fourth.__pad, (void **) ptr)) ++ return -EFAULT; ++ return sys_semctl(first, second, third, fourth); ++ } ++ default: ++ return -EINVAL; ++ } ++ if (call <= MSGCTL) ++ switch (call) { ++ case MSGSND: ++ return sys_msgsnd(first, (struct msgbuf *) ptr, ++ second, third); ++ case MSGRCV: ++ switch (version) { ++ case 0: { ++ struct ipc_kludge tmp; ++ if (!ptr) ++ return -EINVAL; ++ if (copy_from_user(&tmp, ++ (struct ipc_kludge *)ptr, ++ sizeof(tmp))) ++ return -EFAULT; ++ return sys_msgrcv(first, tmp.msgp, second, ++ tmp.msgtyp, third); ++ } ++ default: ++ return sys_msgrcv(first, ++ (struct msgbuf *) ptr, ++ second, fifth, third); ++ } ++ case MSGGET: ++ return sys_msgget((key_t) first, second); ++ case MSGCTL: ++ return sys_msgctl(first, second, ++ (struct msqid_ds *) ptr); ++ default: ++ return -EINVAL; ++ } ++ if (call <= SHMCTL) ++ switch (call) { ++ case SHMAT: ++ switch (version) { ++ default: { ++ ulong raddr; ++ ret = do_shmat(first, ptr, second, &raddr); ++ if (ret) ++ return ret; ++ return put_user(raddr, (ulong __user *) third); ++ } ++ } ++ case SHMDT: ++ return sys_shmdt(ptr); ++ case SHMGET: ++ return sys_shmget(first, second, third); ++ case SHMCTL: ++ return sys_shmctl(first, second, ptr); ++ default: ++ return -ENOSYS; ++ } ++ ++ return -EINVAL; ++} ++ ++/* sys_cacheflush -- flush (part of) the processor cache. */ ++asmlinkage int ++sys_cacheflush(unsigned long addr, int scope, int cache, unsigned long len) ++{ ++ flush_cache_all(); ++ return 0; ++} ++ ++asmlinkage int sys_getpagesize(void) ++{ ++ return PAGE_SIZE; ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/thread.c +@@ -0,0 +1,228 @@ ++/* ++ * arch/ubicom32/kernel/thread.c ++ * Ubicom32 architecture hardware thread support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/profile.h> ++#include <linux/clocksource.h> ++#include <linux/types.h> ++#include <asm/ip5000.h> ++#include <asm/machdep.h> ++#include <asm/asm-offsets.h> ++#include <asm/thread.h> ++ ++/* ++ * TODO: At some point change the name here to be thread_ksp ++ */ ++unsigned int sw_ksp[THREAD_ARCHITECTURAL_MAX]; ++ ++static unsigned int thread_mask = -1; ++static unsigned int thread_mainline_mask; ++ ++/* ++ * thread_entry() ++ * Returning from the called function will disable the thread. ++ * ++ * This could be a naked call to allow for hwthreads that do not have stacks. ++ * However, with -O0, the code still writes to thex stack, and this was ++ * corrupting memory just after the callers stack. ++ */ ++static void thread_entry(void *arg, thread_exec_fn_t exec) ++{ ++ /* ++ * Call thread function ++ */ ++ exec(arg); ++ ++ /* ++ * Complete => Disable self ++ */ ++ thread_disable(thread_get_self()); ++} ++ ++/* ++ * thread_start() ++ * Start the specified function on the specified hardware thread. ++ */ ++thread_t thread_start(thread_t thread, ++ thread_exec_fn_t exec, ++ void *arg, ++ unsigned int *sp_high, ++ thread_type_t type) ++{ ++ /* ++ * Sanity check ++ */ ++ unsigned int enabled, mask, csr; ++ asm volatile ( ++ "move.4 %0, MT_EN\n\t" ++ : "=m" (enabled) ++ ); ++ ++ mask = 1 << thread; ++ if (enabled & mask) { ++ printk(KERN_WARNING "request to enable a previously enabled thread\n"); ++ return (thread_t)-1; ++ } ++ ++ /* ++ * Update thread state ++ */ ++ csr = (thread << 15) | (1 << 14); ++ asm volatile ( ++ "setcsr %0 \n\t" ++ "setcsr_flush 0 \n\t" ++ ++ "move.4 A0, #0 \n\t" ++ "move.4 A1, #0 \n\t" ++ "move.4 A2, #0 \n\t" ++ "move.4 A3, #0 \n\t" ++ "move.4 A4, #0 \n\t" ++ "move.4 A5, #0 \n\t" ++ "move.4 A6, #0 \n\t" ++ "move.4 SP, %4 \n\t" /* A7 is SP */ ++ ++ "move.4 D0, %3 \n\t" ++ "move.4 D1, %2 \n\t" ++ "move.4 D2, #0 \n\t" ++ "move.4 D3, #0 \n\t" ++ "move.4 D4, #0 \n\t" ++ "move.4 D5, #0 \n\t" ++ "move.4 D6, #0 \n\t" ++ "move.4 D7, #0 \n\t" ++ "move.4 D8, #0 \n\t" ++ "move.4 D9, #0 \n\t" ++ "move.4 D10, #0 \n\t" ++ "move.4 D11, #0 \n\t" ++ "move.4 D12, #0 \n\t" ++ "move.4 D13, #0 \n\t" ++ "move.4 D14, #0 \n\t" ++ "move.4 D15, #0 \n\t" ++ ++ "move.4 INT_MASK0, #0 \n\t" ++ "move.4 INT_MASK1, #0 \n\t" ++ "move.4 PC, %1 \n\t" ++ "setcsr #0 \n\t" ++ "setcsr_flush 0 \n\t" ++ : ++ : "r" (csr), "r" (thread_entry), "r" (exec), ++ "r" (arg), "r" (sp_high) ++ ); ++ ++ /* ++ * Apply HRT state ++ */ ++ if (type & THREAD_TYPE_HRT) { ++ asm volatile ( ++ "or.4 MT_HRT, MT_HRT, %0\n\t" ++ : ++ : "d" (mask) ++ : "cc" ++ ); ++ } else { ++ asm volatile ( ++ "and.4 MT_HRT, MT_HRT, %0\n\t" ++ : ++ : "d" (~mask) ++ : "cc" ++ ); ++ } ++ ++ /* ++ * Set priority ++ */ ++ asm volatile ( ++ "or.4 MT_HPRI, MT_HPRI, %0\n\t" ++ : ++ : "d" (mask) ++ : "cc" ++ ); ++ ++ /* ++ * Enable thread ++ */ ++ asm volatile ( ++ "move.4 MT_ACTIVE_SET, %0 \n\t" ++ : ++ : "d" (mask) ++ ); ++ thread_enable_mask(mask); ++ return thread; ++} ++ ++/* ++ * thread_get_mainline() ++ * Return a mask of those threads that are Linux mainline threads. ++ */ ++unsigned int thread_get_mainline(void) ++{ ++ return thread_mainline_mask; ++} ++ ++/* ++ * thread_set_mainline() ++ * Indicate that the specified thread is a Linux mainline thread. ++ */ ++void thread_set_mainline(thread_t tid) ++{ ++ thread_mainline_mask |= (1 << tid); ++} ++ ++/* ++ * thread_alloc() ++ * Allocate an unused hardware thread. ++ */ ++thread_t thread_alloc(void) ++{ ++ thread_t tid; ++ ++ /* ++ * If this is the first time we are here get the list of unused ++ * threads from the processor device tree node. ++ */ ++ if (thread_mask == -1) { ++ thread_mask = processor_threads(); ++ } ++ ++ if (!thread_mask) { ++ return (thread_t)-1; ++ } ++ ++ tid = ffs(thread_mask); ++ if (tid != 0) { ++ tid--; ++ thread_mask &= ~(1 << tid); ++ return tid; ++ } ++ ++ return (thread_t)-1; ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/time.c +@@ -0,0 +1,212 @@ ++/* ++ * arch/ubicom32/kernel/time.c ++ * Initialize the timer list and start the appropriate timers. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1991, 1992, 1995 Linus Torvalds ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/profile.h> ++#include <linux/smp.h> ++#include <asm/ip5000.h> ++#include <asm/machdep.h> ++ ++/* ++ * A bitmap of the timers on the processor indicates ++ * that the timer is free or in-use. ++ */ ++static unsigned int timers; ++ ++/* ++ * timer_set() ++ * Init the specified compare register to go off <n> cycles from now. ++ */ ++void timer_set(int timervector, unsigned int cycles) ++{ ++ int idx = UBICOM32_VECTOR_TO_TIMER_INDEX(timervector); ++ UBICOM32_IO_TIMER->syscom[idx] = ++ UBICOM32_IO_TIMER->sysval + cycles; ++ ldsr_enable_vector(timervector); ++} ++ ++/* ++ * timer_reset() ++ * Set/reset the timer to go off again. ++ * ++ * Because sysval is a continuous timer, this function is able ++ * to ensure that we do not have clock sku by using the previous ++ * value in syscom to set the next value for syscom. ++ * ++ * Returns the number of ticks that transpired since the last event. ++ */ ++int timer_reset(int timervector, unsigned int cycles) ++{ ++ /* ++ * Reset the timer in the LDSR thread to go off appropriately. ++ * ++ * Use the previous value of the timer to calculate the new stop ++ * time. This allows us to account for it taking an ++ * indeterminate amount of time to get here. ++ */ ++ const int timer_index = UBICOM32_VECTOR_TO_TIMER_INDEX(timervector); ++ unsigned int prev = UBICOM32_IO_TIMER->syscom[timer_index]; ++ unsigned int next = prev + cycles; ++ int scratchpad3; ++ int diff; ++ int ticks = 1; ++ ++ /* ++ * If the difference is negative, we have missed at least one ++ * timer tick. ++ * ++ * TODO: Decide if we want to "ignore" time (as done below) or ++ * if we want to process time (unevenly) by calling timer_tick() ++ * lost_ticks times. ++ */ ++ while (1) { ++ /* ++ * Set our future time first. ++ */ ++ UBICOM32_IO_TIMER->syscom[timer_index] = next; ++ ++ /* ++ * Then check if we are really set time in the futrue. ++ */ ++ diff = (int)next - (int)UBICOM32_IO_TIMER->sysval; ++ if (diff >= 0) { ++ break; ++ } ++ ++ /* ++ * Oops, we are too slow. Playing catch up. ++ * ++ * If the debugger is connected the there is a good ++ * chance that we lost time because we were in a ++ * break-point, so in this case we do not print out ++ * diagnostics. ++ */ ++ asm volatile ("move.4 %0, scratchpad3" ++ : "=r" (scratchpad3)); ++ if ((scratchpad3 & 0x1) == 0) { ++ /* ++ * No debugger attached, print to the console ++ */ ++ printk(KERN_EMERG "diff: %d, timer has lost %u " ++ "ticks [rounded up]\n", ++ -diff, ++ (unsigned int)((-diff + cycles - 1) / cycles)); ++ } ++ ++ do { ++ next += cycles; ++ diff = (int)next - (int)UBICOM32_IO_TIMER->sysval; ++ ticks++; ++ } while (diff < 0); ++ } ++ return ticks; ++} ++ ++/* ++ * sched_clock() ++ * Returns current time in nano-second units. ++ * ++ * Notes: ++ * 1) This is an override for the weak alias in ++ * kernel/sched_clock.c. ++ * 2) Do not use xtime_lock as this function is ++ * sometimes called with xtime_lock held. ++ * 3) We use a retry algorithm to ensure that ++ * we get a consistent value. ++ * 4) sched_clock must be overwritten if IRQ tracing ++ * is enabled because the default implementation uses ++ * the xtime_lock sequence while holding xtime_lock. ++ */ ++unsigned long long sched_clock(void) ++{ ++ unsigned long long my_jiffies; ++ unsigned long jiffies_top; ++ unsigned long jiffies_bottom; ++ ++ do { ++ jiffies_top = jiffies_64 >> 32; ++ jiffies_bottom = jiffies_64 & 0xffffffff; ++ } while (unlikely(jiffies_top != (unsigned long)(jiffies_64 >> 32))); ++ ++ my_jiffies = ((unsigned long long)jiffies_top << 32) | (jiffies_bottom); ++ return (my_jiffies - INITIAL_JIFFIES) * (NSEC_PER_SEC / HZ); ++} ++ ++/* ++ * timer_free() ++ * Free a hardware timer. ++ */ ++void timer_free(int interrupt) ++{ ++ unsigned int bit = interrupt - TIMER_INT(0); ++ ++ /* ++ * The timer had not been allocated. ++ */ ++ BUG_ON(timers & (1 << bit)); ++ timers |= (1 << bit); ++} ++ ++/* ++ * timer_alloc() ++ * Allocate a hardware timer. ++ */ ++int timer_alloc(void) ++{ ++ unsigned int bit = find_first_bit((unsigned long *)&timers, 32); ++ if (!bit) { ++ printk(KERN_WARNING "no more free timers\n"); ++ return -1; ++ } ++ ++ timers &= ~(1 << bit); ++ return bit + TIMER_INT(0); ++} ++ ++/* ++ * time_init() ++ * Time init function. ++ */ ++void time_init(void) ++{ ++ /* ++ * Find the processor node and determine what timers are ++ * available for us. ++ */ ++ timers = processor_timers(); ++ if (timers == 0) { ++ printk(KERN_WARNING "no timers are available for Linux\n"); ++ return; ++ } ++ ++#ifdef CONFIG_GENERIC_CLOCKEVENTS ++ timer_device_init(); ++#else ++ timer_tick_init(); ++#endif ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/timer_broadcast.c +@@ -0,0 +1,102 @@ ++/* ++ * arch/ubicom32/kernel/timer_broadcast.c ++ * Implements a dummy clock event for each cpu. ++ * ++ * Copyright (C) 2008 Paul Mundt ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * arch/arm ++ * arch/sh ++ */ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/smp.h> ++#include <linux/jiffies.h> ++#include <linux/percpu.h> ++#include <linux/clockchips.h> ++#include <linux/irq.h> ++ ++static DEFINE_PER_CPU(struct clock_event_device, local_clockevent); ++ ++/* ++ * The broadcast trick only works when the timer will be used in a periodic mode. ++ * If the user has configured either NO_HZ or HIGH_RES_TIMERS they must have ++ * a per cpu timer. ++ */ ++#if defined(CONFIG_NO_HZ) || defined(CONFIG_HIGH_RES_TIMERS) ++#error "Tickless and High Resolution Timers require per-CPU local timers: CONFIG_LOCAL_TIMERS" ++#endif ++ ++/* ++ * local_timer_interrupt() ++ * Used on SMP for local timer interrupt sent via an IPI. ++ */ ++void local_timer_interrupt(void) ++{ ++ struct clock_event_device *dev = &__get_cpu_var(local_clockevent); ++ ++ dev->event_handler(dev); ++} ++ ++/* ++ * dummy_timer_set_next_event() ++ * Cause the timer to go off "cycles" from now. ++ */ ++static int dummy_timer_set_next_event(unsigned long cycles, struct clock_event_device *dev) ++{ ++ return 0; ++} ++ ++/* ++ * dummy_timer_set_mode() ++ * Do Nothing. ++ */ ++static void dummy_timer_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *clk) ++{ ++} ++ ++/* ++ * local_timer_setup() ++ * Adds a clock event for the specified cpu. ++ */ ++int __cpuinit local_timer_setup(unsigned int cpu) ++{ ++ struct clock_event_device *dev = &per_cpu(local_clockevent, cpu); ++ ++ dev->name = "timer-dummy"; ++ dev->features = CLOCK_EVT_FEAT_DUMMY; ++ dev->rating = 200; ++ dev->mult = 1; ++ dev->set_mode = dummy_timer_set_mode; ++ dev->set_next_event = dummy_timer_set_next_event; ++ dev->broadcast = smp_timer_broadcast; ++ dev->cpumask = cpumask_of_cpu(cpu); ++ dev->irq = -1; ++ printk(KERN_NOTICE "timer[%d]: %s - created\n", dev->irq, dev->name); ++ ++ clockevents_register_device(dev); ++ return 0; ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/timer_device.c +@@ -0,0 +1,302 @@ ++/* ++ * arch/ubicom32/kernel/timer_device.c ++ * Implements a Ubicom32 clock device and event devices. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/types.h> ++#include <linux/clockchips.h> ++#include <linux/clocksource.h> ++#include <linux/spinlock.h> ++#include <asm/ip5000.h> ++#include <asm/machdep.h> ++ ++#if defined(CONFIG_SMP) ++#include <asm/smp.h> ++#endif ++ ++#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) ++#define MAX_TIMERS (2 + CONFIG_TIMER_EXTRA_ALLOC) ++#else ++#define MAX_TIMERS (NR_CPUS + CONFIG_TIMER_EXTRA_ALLOC) ++#endif ++ ++#if (MAX_TIMERS > 10) ++#error "Ubicom32 only has 10 timers" ++#endif ++ ++static unsigned int frequency; ++static struct clock_event_device timer_device_devs[MAX_TIMERS]; ++static struct irqaction timer_device_irqs[MAX_TIMERS]; ++static int timer_device_next_timer = 0; ++ ++DEFINE_SPINLOCK(timer_device_lock); ++ ++/* ++ * timer_device_set_next_event() ++ * Cause the timer to go off "cycles" from now. ++ */ ++static int timer_device_set_next_event(unsigned long cycles, struct clock_event_device *dev) ++{ ++ timer_set(dev->irq, cycles); ++ return 0; ++} ++ ++/* ++ * timer_device_set_mode() ++ * Handle the mode switch for a clock event device. ++ */ ++static void timer_device_set_mode(enum clock_event_mode mode, struct clock_event_device *dev) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ /* ++ * Make sure the vector is disabled ++ * until the next event is set. ++ */ ++ printk(KERN_NOTICE "timer[%d]: shutdown\n", dev->irq); ++ ldsr_disable_vector(dev->irq); ++ break; ++ ++ case CLOCK_EVT_MODE_ONESHOT: ++ /* ++ * Make sure the vector is disabled ++ * until the next event is set. ++ */ ++ printk(KERN_NOTICE "timer[%d]: oneshot\n", dev->irq); ++ ldsr_disable_vector(dev->irq); ++ break; ++ ++ case CLOCK_EVT_MODE_PERIODIC: ++ /* ++ * The periodic request is 1 per jiffies ++ */ ++ printk(KERN_NOTICE "timer[%d]: periodic: %d cycles\n", ++ dev->irq, frequency / CONFIG_HZ); ++ timer_set(dev->irq, frequency / CONFIG_HZ); ++ break; ++ ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_RESUME: ++ printk(KERN_WARNING "timer[%d]: unimplemented mode: %d\n", ++ dev->irq, mode); ++ break; ++ }; ++} ++ ++/* ++ * timer_device_event() ++ * Call the device's event handler. ++ * ++ * The pointer is initialized by the generic Linux code ++ * to the function to be called. ++ */ ++static irqreturn_t timer_device_event(int irq, void *dev_id) ++{ ++ struct clock_event_device *dev = (struct clock_event_device *)dev_id; ++ ++ if (dev->mode == CLOCK_EVT_MODE_PERIODIC) { ++ /* ++ * The periodic request is 1 per jiffies ++ */ ++ timer_reset(dev->irq, frequency / CONFIG_HZ); ++ } else { ++ /* ++ * The timer will go off again at the rollover ++ * point. We must disable the IRQ to prevent ++ * getting a spurious interrupt. ++ */ ++ ldsr_disable_vector(dev->irq); ++ } ++ ++ if (!dev->event_handler) { ++ printk(KERN_CRIT "no registered event handler\n"); ++ return IRQ_HANDLED; ++ } ++ ++ dev->event_handler(dev); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * timer_device_clockbase_read() ++ * Provide a primary clocksource around the sysval timer. ++ */ ++static cycle_t timer_device_clockbase_read(void) ++{ ++ return (cycle_t)UBICOM32_IO_TIMER->sysval; ++} ++ ++/* ++ * Primary Clock Source Description ++ * ++ * We use 24 for the shift factor because we want ++ * to ensure there are less than 2^24 clocks ++ * in a jiffie of 10 ms. ++ */ ++static struct clocksource timer_device_clockbase = { ++ .name = "sysval", ++ .rating = 400, ++ .flags = CLOCK_SOURCE_IS_CONTINUOUS, ++ .mask = CLOCKSOURCE_MASK(32), ++ .shift = 24, ++ .mult = 0, ++ .read = timer_device_clockbase_read, ++}; ++ ++/* ++ * timer_device_alloc_event() ++ * Allocate a timer device event. ++ */ ++static int timer_device_alloc_event(const char *name, int cpuid, cpumask_t mask) ++{ ++ struct clock_event_device *dev; ++ struct irqaction *action; ++ ++ /* ++ * Are we out of configured timers? ++ */ ++ spin_lock(&timer_device_lock); ++ if (timer_device_next_timer >= MAX_TIMERS) { ++ spin_unlock(&timer_device_lock); ++ printk(KERN_WARNING "out of timer event entries\n"); ++ return -1; ++ } ++ dev = &timer_device_devs[timer_device_next_timer]; ++ action = &timer_device_irqs[timer_device_next_timer]; ++ timer_device_next_timer++; ++ spin_unlock(&timer_device_lock); ++ ++ /* ++ * Now allocate a timer to ourselves. ++ */ ++ dev->irq = timer_alloc(); ++ if (dev->irq == -1) { ++ spin_lock(&timer_device_lock); ++ timer_device_next_timer--; ++ spin_unlock(&timer_device_lock); ++ printk(KERN_WARNING "out of hardware timers\n"); ++ return -1; ++ } ++ ++ /* ++ * Init the IRQ action structure. Make sure ++ * this in place before you register the clock ++ * event device. ++ */ ++ action->name = name; ++ action->flags = IRQF_DISABLED | IRQF_TIMER; ++ action->handler = timer_device_event; ++ action->mask = mask; ++ action->dev_id = dev; ++ setup_irq(dev->irq, action); ++ irq_set_affinity(dev->irq, mask); ++ ldsr_disable_vector(dev->irq); ++ ++ /* ++ * init clock dev structure. ++ * ++ * The min_delta_ns is chosen to ensure that setting next ++ * event will never be requested with too small of value. ++ */ ++ dev->name = name; ++ dev->rating = timer_device_clockbase.rating; ++ dev->shift = timer_device_clockbase.shift; ++ dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; ++ dev->set_mode = timer_device_set_mode; ++ dev->set_next_event = timer_device_set_next_event; ++ dev->mult = div_sc(frequency, NSEC_PER_SEC, dev->shift); ++ dev->max_delta_ns = clockevent_delta2ns(0xffffffff, dev); ++ dev->min_delta_ns = clockevent_delta2ns(100, dev); ++ dev->cpumask = mask; ++ printk(KERN_NOTICE "timer[%d]: %s - created\n", dev->irq, dev->name); ++ ++ /* ++ * Now register the device. ++ */ ++ clockevents_register_device(dev); ++ return dev->irq; ++} ++ ++#if defined(CONFIG_LOCAL_TIMERS) ++/* ++ * local_timer_setup() ++ * Allocation function for creating a per cpu local timer. ++ */ ++int __cpuinit local_timer_setup(unsigned int cpu) ++{ ++ return timer_device_alloc_event("timer-cpu", cpu, cpumask_of_cpu(cpu)); ++} ++#endif ++ ++/* ++ * timer_device_init() ++ * Create and init a generic clock driver for Ubicom32. ++ */ ++void timer_device_init(void) ++{ ++ int i; ++ ++ /* ++ * Get the frequency from the processor device tree node or use ++ * the default if not available. We will store this as the frequency ++ * of the timer to avoid future calculations. ++ */ ++ frequency = processor_frequency(); ++ if (frequency == 0) { ++ frequency = CLOCK_TICK_RATE; ++ } ++ ++ /* ++ * Setup the primary clock source around sysval. Linux does not ++ * supply a Mhz multiplier so convert down to khz. ++ */ ++ timer_device_clockbase.mult = ++ clocksource_khz2mult(frequency / 1000, ++ timer_device_clockbase.shift); ++ if (clocksource_register(&timer_device_clockbase)) { ++ printk(KERN_ERR "timer: clocksource failed to register\n"); ++ return; ++ } ++ ++ /* ++ * Always allocate a primary timer. ++ */ ++ timer_device_alloc_event("timer-primary", -1, CPU_MASK_ALL); ++ ++#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) ++ /* ++ * If BROADCAST is selected we need to add a broadcast timer. ++ */ ++ timer_device_alloc_event("timer-broadcast", -1, CPU_MASK_ALL); ++#endif ++ ++ /* ++ * Allocate extra timers that are requested. ++ */ ++ for (i = 0; i < CONFIG_TIMER_EXTRA_ALLOC; i++) { ++ timer_device_alloc_event("timer-extra", -1, CPU_MASK_ALL); ++ } ++} ++ +--- /dev/null ++++ b/arch/ubicom32/kernel/timer_tick.c +@@ -0,0 +1,109 @@ ++/* ++ * arch/ubicom32/kernel/timer_tick.c ++ * Impelemets a perodic timer. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1991, 1992, 1995 Linus Torvalds ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/profile.h> ++ ++#include <asm/ip5000.h> ++#include <asm/machdep.h> ++#if defined(CONFIG_SMP) ++#include <asm/smp.h> ++#endif ++ ++static unsigned int timervector; ++static unsigned int frequency; ++ ++/* ++ * timer_tick() ++ * Kernel system timer support. Needs to keep up the real-time clock, ++ * as well as call the "do_timer()" routine every clocktick. ++ */ ++static irqreturn_t timer_tick(int irq, void *dummy) ++{ ++ int ticks; ++ ++ BUG_ON(!irqs_disabled()); ++ ticks = timer_reset(timervector, frequency); ++ ++ write_seqlock(&xtime_lock); ++ do_timer(ticks); ++ write_sequnlock(&xtime_lock); ++ ++ update_process_times(user_mode(get_irq_regs())); ++ profile_tick(CPU_PROFILING); ++ ++#if defined(CONFIG_SMP) ++ smp_send_timer_all(); ++#endif ++ return(IRQ_HANDLED); ++} ++ ++/* ++ * Data used by setup_irq for the timer. ++ */ ++static struct irqaction timer_irq = { ++ .name = "timer", ++ .flags = IRQF_DISABLED | IRQF_TIMER, ++ .handler = timer_tick, ++}; ++ ++/* ++ * timer_tick_init() ++ * Implements a periodic timer ++ * ++ * This implementation directly calls the timer_tick() and move ++ * the Linux kernel forward. This is used when the user has not ++ * selected GENERIC_CLOCKEVENTS. ++ */ ++void timer_tick_init(void) ++{ ++ /* ++ * Now allocate a timer to ourselves. ++ */ ++ timervector = timer_alloc(); ++ if (timervector == -1) { ++ printk(KERN_WARNING "where did the timer go?\n"); ++ return; ++ } ++ ++ setup_irq(timervector, &timer_irq); ++ ++ /* ++ * Get the frequency from the processor device tree node or use ++ * the default if not available. We will store this as the frequency ++ * of the timer to avoid future calculations. ++ */ ++ frequency = processor_frequency(); ++ if (frequency == 0) { ++ frequency = CLOCK_TICK_RATE; ++ } ++ frequency /= CONFIG_HZ; ++ ++ printk(KERN_NOTICE "timer will interrupt every: %d cycles\n", frequency); ++ timer_set(timervector, frequency); ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/topology.c +@@ -0,0 +1,47 @@ ++/* ++ * arch/ubicom32/kernel/topology.c ++ * Ubicom32 architecture sysfs topology information. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/init.h> ++#include <linux/smp.h> ++#include <linux/cpu.h> ++#include <linux/cache.h> ++ ++static struct cpu cpu_devices[NR_CPUS] __read_mostly; ++ ++static int __init topology_init(void) ++{ ++ int num; ++ ++ for_each_present_cpu(num) { ++ cpu_devices[num].hotpluggable = 0; ++ register_cpu(&cpu_devices[num], num); ++ } ++ return 0; ++} ++ ++subsys_initcall(topology_init); +--- /dev/null ++++ b/arch/ubicom32/kernel/traps.c +@@ -0,0 +1,510 @@ ++/* ++ * arch/ubicom32/kernel/traps.c ++ * Ubicom32 architecture trap handling support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++/* ++ * Sets up all exception vectors ++ */ ++#include <linux/sched.h> ++#include <linux/signal.h> ++#include <linux/kernel.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/a.out.h> ++#include <linux/user.h> ++#include <linux/string.h> ++#include <linux/linkage.h> ++#include <linux/init.h> ++#include <linux/ptrace.h> ++#include <linux/kallsyms.h> ++#include <linux/compiler.h> ++#include <linux/stacktrace.h> ++#include <linux/personality.h> ++ ++#include <asm/uaccess.h> ++#include <asm/stacktrace.h> ++#include <asm/devtree.h> ++#include <asm/setup.h> ++#include <asm/fpu.h> ++#include <asm/system.h> ++#include <asm/traps.h> ++#include <asm/pgtable.h> ++#include <asm/machdep.h> ++#include <asm/siginfo.h> ++#include <asm/ip5000.h> ++#include <asm/thread.h> ++ ++#define TRAP_MAX_STACK_DEPTH 20 ++ ++/* ++ * These symbols are filled in by the linker. ++ */ ++extern unsigned long _stext; ++extern unsigned long _etext; ++ ++extern unsigned long __ocm_text_run_begin; ++extern unsigned long __data_begin; ++ ++const char *trap_cause_strings[] = { ++ /*0*/ "inst address decode error", ++ /*1*/ "inst sync error", ++ /*2*/ "inst illegal", ++ /*3*/ "src1 address decode error", ++ /*4*/ "dst address decode error", ++ /*5*/ "src1 alignment error", ++ /*6*/ "dst alignment error", ++ /*7*/ "src1 sync error", ++ /*8*/ "dst sync error", ++ /*9*/ "DCAPT error", ++ /*10*/ "inst range error", ++ /*11*/ "src1 range error", ++ /*12*/ "dst range error", ++}; ++ ++/* ++ * The device tree trap node definition. ++ */ ++struct trapnode { ++ struct devtree_node dn; ++ unsigned int intthread; ++}; ++ ++static struct trapnode *tn;; ++ ++/* ++ * trap_interrupt_handler() ++ * Software Interrupt to ensure that a trap is serviced. ++ */ ++static irqreturn_t trap_interrupt_handler(int irq, void *dummy) ++{ ++ /* Do Nothing */ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Data used by setup_irq for the timer. ++ */ ++static struct irqaction trap_irq = { ++ .name = "trap", ++ .flags = IRQF_DISABLED, ++ .handler = trap_interrupt_handler, ++}; ++ ++/* ++ * trap_cause_to_str() ++ * Convert a trap_cause into a series of printk ++ */ ++static void trap_cause_to_str(long status) ++{ ++ int bit; ++ ++ if ((status & ((1 << TRAP_CAUSE_TOTAL) - 1)) == 0) { ++ printk(KERN_NOTICE "decode: UNKNOWN CAUSES\n"); ++ return; ++ } ++ ++ for (bit = 0; bit < TRAP_CAUSE_TOTAL; bit++) { ++ if (status & (1 << bit)) { ++ printk(KERN_NOTICE "\tdecode: %08x %s\n", ++ 1 << bit, trap_cause_strings[bit]); ++ } ++ } ++} ++ ++/* ++ * trap_print_information() ++ * Print the cause of the trap and additional info. ++ */ ++static void trap_print_information(const char *str, struct pt_regs *regs) ++{ ++ printk(KERN_WARNING "\n"); ++ ++ if (current) { ++ printk(KERN_WARNING "Process %s (pid: %d)\n", ++ current->comm, current->pid); ++ } ++ ++ if (current && current->mm) { ++ printk(KERN_NOTICE "text = 0x%p-0x%p data = 0x%p-0x%p\n" ++ KERN_NOTICE "bss = 0x%p-0x%p user-stack = 0x%p\n" ++ KERN_NOTICE "\n", ++ (void *)current->mm->start_code, ++ (void *)current->mm->end_code, ++ (void *)current->mm->start_data, ++ (void *)current->mm->end_data, ++ (void *)current->mm->end_data, ++ (void *)current->mm->brk, ++ (void *)current->mm->start_stack); ++ } ++ ++ printk(KERN_WARNING "%s: Causes: 0x%08x\n", str, ++ (unsigned int)regs->trap_cause); ++ trap_cause_to_str(regs->trap_cause); ++ show_regs(regs); ++ show_stack(NULL, (unsigned long *)regs->an[7]); ++ printk(KERN_NOTICE "--- End Trap --- \n"); ++} ++ ++/* ++ * dump_stack() ++ * Dump the stack of the current task. ++ */ ++void dump_stack(void) ++{ ++ show_stack(NULL, NULL); ++} ++EXPORT_SYMBOL(dump_stack); ++ ++/* ++ * show_stack() ++ * Print out information from the current stack. ++ */ ++void show_stack(struct task_struct *task, unsigned long *sp) ++{ ++ /* ++ * Allocate just enough entries on the stack. ++ */ ++ unsigned int calls[TRAP_MAX_STACK_DEPTH]; ++ unsigned long code_start = (unsigned long)CONFIG_RAMBASE; ++ unsigned long code_end = (unsigned long)CONFIG_RAMBASE+CONFIG_RAMSIZE; ++ unsigned long ocm_code_start = (unsigned long)&__ocm_text_run_begin; ++ unsigned long ocm_code_end = (unsigned long)&__data_begin; ++ unsigned long stack_end = (unsigned long)(current->stack + THREAD_SIZE - 8); ++ unsigned long stack = (unsigned long)sp; ++ int kernel_stack = 1; ++ ++ /* ++ * Which task are we talking about. ++ */ ++ if (!task) { ++ task = current; ++ } ++ ++ /* ++ * Find the stack for the task if one was not specified. Otherwise ++ * use the specified stack. ++ */ ++ if (!stack) { ++ if (task != current) { ++ stack = task->thread.sp; ++ stack_end = (unsigned long)task->stack + THREAD_SIZE - 8; ++ } else { ++ asm volatile ( ++ "move.4 %0, SP \n\t" ++ : "=r" (stack) ++ ); ++ } ++ } ++ ++ printk(KERN_NOTICE "Starting backtrace: PID %d '%s'\n", ++ task->pid, task->comm); ++ ++ /* ++ * We do 2 passes the first pass is Kernel stack is the second ++ * User stack. ++ */ ++ while (kernel_stack) { ++ unsigned long *handle; ++ unsigned int i, idx = 0; ++ struct pt_regs *pt = task_pt_regs(task); ++ ++ /* ++ * If the task is in user mode, reset the start ++ * and end values for text. ++ */ ++ if (__user_mode(stack)) { ++ if (!(task->personality & FDPIC_FUNCPTRS)) { ++ printk(KERN_NOTICE " User Stack:\n"); ++ code_start = task->mm->start_code; ++ code_end = task->mm->end_code; ++ } else { ++ printk(KERN_NOTICE " User Stack (fdpic):\n"); ++ ++ } ++ stack_end = task->mm->start_stack; ++ ocm_code_end = ocm_code_start = 0; ++ kernel_stack = 0; ++ } else { ++ printk(KERN_NOTICE " Kernel Stack:\n"); ++ } ++ ++ /* ++ * Collect the stack back trace information. ++ */ ++ printk(" code[0x%lx-0x%lx]", code_start, code_end); ++ if (ocm_code_start) { ++ printk(" ocm_code[0x%lx-0x%lx]", ++ ocm_code_start, ocm_code_end); ++ } ++ printk("\n stack[0x%lx-0x%lx]\n", stack, stack_end); ++ ++ handle = (unsigned long*)stack; ++ while (idx < TRAP_MAX_STACK_DEPTH) { ++ calls[idx] = stacktrace_iterate(&handle, ++ code_start, code_end, ++ ocm_code_start, ocm_code_end, ++ (unsigned long)stack, stack_end); ++ if (calls[idx] == 0) { ++ break; ++ } ++ idx++; ++ } ++ ++ /* ++ * Now print out the data. ++ */ ++ printk(KERN_NOTICE " CALL && CALLI on stack:"); ++ for (i = 0; i < idx; i++) { ++ printk("%s0x%x, ", (i & 0x3) == 0 ? "\n " : "", ++ calls[i]); ++ } ++ printk(idx == TRAP_MAX_STACK_DEPTH ? "...\n" : "\n"); ++ ++ /* ++ * If we are doing user stack we are done ++ */ ++ if (!kernel_stack) { ++ break; ++ } ++ ++ /* ++ * Does this kernel stack have a mm (i.e. is it user) ++ */ ++ if (!task->mm) { ++ printk("No mm for userspace stack.\n"); ++ break; ++ } ++ /* ++ * Get the user-mode stack (if any) ++ */ ++ stack = pt->an[7]; ++ printk(KERN_NOTICE "Userspace stack at 0x%lx frame type %d\n", ++ stack, (int)pt->frame_type); ++ if (!__user_mode(stack)) { ++ break; ++ } ++ } ++} ++ ++/* ++ * die_if_kernel() ++ * Determine if we are in kernel mode and if so print stuff out and die. ++ */ ++void die_if_kernel(char *str, struct pt_regs *regs, long trap_cause) ++{ ++ unsigned int s3value; ++ ++ if (user_mode(regs)) { ++ return; ++ } ++ ++ console_verbose(); ++ trap_print_information(str, regs); ++ ++ /* ++ * If the debugger is attached via the hardware mailbox protocol, ++ * go into an infinite loop and the debugger will figure things out. ++ */ ++ asm volatile ( ++ "move.4 %0, scratchpad3" ++ : "=r" (s3value) ++ ); ++ if (s3value) { ++ asm volatile("1: jmpt.t 1b"); ++ } ++ ++ /* ++ * Set the debug taint value. ++ */ ++ add_taint(TAINT_DIE); ++ do_exit(SIGSEGV); ++} ++ ++/* ++ * trap_handler() ++ * Handle traps. ++ * ++ * Traps are treated as interrupts and registered with the LDSR. When ++ * the LDSR takes the interrupt, it will determine if a trap has occurred ++ * and service the trap prior to servicing the interrupt. ++ * ++ * This function is directly called by the LDSR. ++ */ ++void trap_handler(int irq, struct pt_regs *regs) ++{ ++ int sig = SIGSEGV; ++ siginfo_t info; ++ unsigned int trap_cause = regs->trap_cause; ++ ++ BUG_ON(!irqs_disabled()); ++ ++ /* ++ * test if in kernel and die. ++ */ ++ die_if_kernel("Kernel Trap", regs, trap_cause); ++ ++ /* ++ * User process problem, setup a signal for this process ++ */ ++ if ((trap_cause & (1 << TRAP_CAUSE_DST_RANGE_ERR)) || ++ (trap_cause & (1 << TRAP_CAUSE_SRC1_RANGE_ERR)) || ++ (trap_cause & (1 << TRAP_CAUSE_I_RANGE_ERR))) { ++ sig = SIGSEGV; ++ info.si_code = SEGV_MAPERR; ++ } else if ((trap_cause & (1 << TRAP_CAUSE_DST_MISALIGNED)) || ++ (trap_cause & (1 << TRAP_CAUSE_SRC1_MISALIGNED))) { ++ sig = SIGBUS; ++ info.si_code = BUS_ADRALN; ++ } else if ((trap_cause & (1 << TRAP_CAUSE_DST_DECODE_ERR)) || ++ (trap_cause & (1 << TRAP_CAUSE_SRC1_DECODE_ERR))) { ++ sig = SIGILL; ++ info.si_code = ILL_ILLOPN; ++ } else if ((trap_cause & (1 << TRAP_CAUSE_ILLEGAL_INST))) { ++ /* ++ * Check for software break point and if found signal trap ++ * not illegal instruction. ++ */ ++ unsigned long instruction; ++ if (between(regs->pc, CONFIG_RAMBASE, ++ CONFIG_RAMBASE + CONFIG_RAMSIZE) && ++ (regs->pc & 3) == 0 && ++ get_user(instruction, (unsigned long *)regs->pc) == 0) { ++ ++ /* ++ * This used to be 0xaabbccdd but it turns out ++ * that is now valid in ubicom32v4 isa so we ++ * have switched to 0xfabbccdd ++ */ ++ if ((instruction == 0xfabbccdd) || ++ (instruction == 0xaabbccdd)) { ++ sig = SIGTRAP; ++ info.si_code = TRAP_BRKPT; ++ goto send_signal; ++ } ++ } ++ sig = SIGILL; ++ info.si_code = ILL_ILLOPC; ++ } else if ((trap_cause & (1 << TRAP_CAUSE_I_DECODE_ERR))) { ++ sig = SIGILL; ++ info.si_code = ILL_ILLOPC; ++ } else if ((trap_cause & (1 << TRAP_CAUSE_DCAPT))) { ++ sig = SIGTRAP; ++ info.si_code = TRAP_TRACE; ++ } ++ ++ /* ++ * Print a trap information block to the console, do not ++ * print this above the case because we don't want it ++ * printed for software break points. ++ */ ++ trap_print_information("User Trap", regs); ++ ++send_signal: ++ ++ force_sig_info(sig, &info, current); ++ ++ /* ++ * Interrupts are disabled, re-enable them now. ++ */ ++ if (!irqs_disabled()) { ++ printk(KERN_EMERG "interrupts enabled on exit, irq=%d, regs=%p", ++ irq, regs); ++ BUG(); ++ } ++} ++ ++/* ++ * trap_init_interrupt() ++ * We need a 2nd trap handling init that will occur after init_IRQ(). ++ */ ++void __init trap_init_interrupt(void) ++{ ++ int err; ++ unsigned char tirq; ++ struct devtree_node *dn = (struct devtree_node *)tn; ++ ++ /* ++ * Now setup the Software IRQ so that if a trap occurs the LDSR ++ * is started. The irq is there just to "force" the LDSR to run. ++ */ ++ if (!tn) { ++ printk(KERN_WARNING "trap_init_interrupt skipped.\n"); ++ return; ++ } ++ ++ err = devtree_irq(dn, NULL, &tirq); ++ if (err) { ++ printk(KERN_WARNING "error obtaining trap irq value: %d\n", ++ err); ++ return; ++ } ++ ++ if (tirq == DEVTREE_IRQ_NONE) { ++ printk(KERN_WARNING "trap irq not available: %d\n", tirq); ++ return; ++ } ++ ++ err = setup_irq(tirq, &trap_irq); ++ if (err) { ++ printk(KERN_WARNING "trap irq setup failed: %d\n", err); ++ return; ++ } ++ ++ /* ++ * Let ultra know which thread is handling the traps and ++ * what the interrupt to use is. ++ */ ++ tn->intthread = ldsr_get_threadid(); ++ ++ /* ++ * Tell the LDSR about our IRQ so that it will unsuspend ++ * if one occurs while waiting for the per thread lock. ++ */ ++ ldsr_set_trap_irq(tirq); ++} ++ ++/* ++ * trap_init() ++ * init trap handling ++ * ++ * Trap handling is done through the ldsr. Every time an interrupt ++ * occurs, the LDSR looks for threads that are listed in the TRAP ++ * register and forces a call to the trap handler. ++ */ ++void __init trap_init(void) ++{ ++ /* ++ * If we do not have a trap node in the device tree, we leave the fault ++ * handling to the underlying hardware. ++ */ ++ tn = (struct trapnode *)devtree_find_node("traps"); ++ if (!tn) { ++ printk(KERN_WARNING "traps are not handled by linux\n"); ++ return; ++ } ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/uaccess.c +@@ -0,0 +1,109 @@ ++/* ++ * arch/ubicom32/include/asm/uaccess.c ++ * User space memory access functions for Ubicom32 architecture. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/sched.h> ++#include <linux/mm.h> ++#include <linux/string.h> ++#include <linux/module.h> ++ ++#include <asm/segment.h> ++#include <asm/uaccess.h> ++ ++extern int _stext, _etext, _sdata, _edata, _sbss, _ebss, _end; ++ ++/* ++ * __access_ok() ++ * Check that the address is in the current processes. ++ * ++ * NOTE: The kernel uses "pretend" user addresses that wind ++ * up calling access_ok() so this approach has only marginal ++ * value because you wind up with lots of false positives. ++ */ ++int __access_ok(unsigned long addr, unsigned long size) ++{ ++ // struct vm_area_struct *vma; ++ ++ /* ++ * Don't do anything if we are not a running system yet. ++ */ ++ if (system_state != SYSTEM_RUNNING) { ++ return 1; ++ } ++ ++ /* ++ * It appears that Linux will call this function even when we are not ++ * in the context of a user space application that has a VM address ++ * space. So we must check that current and mm are valid before ++ * performing the check. ++ */ ++ if ((!current) || (!current->mm)) { ++ return 1; ++ } ++ ++ /* ++ * We perform some basic checks on the address to ensure that it ++ * is at least within the range of DRAM. ++ */ ++ if ((addr < (int)&_etext) || (addr > memory_end)) { ++ printk(KERN_WARNING "pid=%d[%s]: range [%lx - %lx] not in memory area: [%lx - %lx]\n", ++ current->pid, current->comm, ++ addr, addr + size, ++ memory_start, memory_end); ++ return 0; ++ } ++ ++ /* ++ * For nommu Linux we can check this by looking at the allowed ++ * memory map for the process. ++ * ++ * TODO: Since the kernel passes addresses in it's own space as though ++ * they were user address, we can not validate the addresses this way. ++ */ ++#if 0 ++ if (!down_read_trylock(¤t->mm->mmap_sem)) { ++ return 1; ++ } ++ vma = find_vma(current->mm, addr); ++ if (!vma) { ++ up_read(¤t->mm->mmap_sem); ++ printk(KERN_WARNING "pid=%d[%s]: possible invalid acesss on range: [%lx - %lx]\n", ++ current->pid, current->comm, addr, addr + size); ++ return 1; ++ } ++ if ((addr + size) > vma->vm_end) { ++ up_read(¤t->mm->mmap_sem); ++ printk(KERN_WARNING "pid=%d[%s]: possible invalid length on range: [%lx - %lx]\n", ++ current->pid, current->comm, addr, addr + size); ++ return 1; ++ } ++ up_read(¤t->mm->mmap_sem); ++#endif ++ return 1; ++} ++ ++EXPORT_SYMBOL(__access_ok); +--- /dev/null ++++ b/arch/ubicom32/kernel/ubicom32_context_switch.S +@@ -0,0 +1,325 @@ ++/* ++ * arch/ubicom32/kernel/ubicom32_context_switch.S ++ * Implements context switch and return functions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/sys.h> ++#include <linux/linkage.h> ++#include <asm/asm-offsets.h> ++#include <asm/ubicom32-common.h> ++#include <asm/ip5000.h> ++#include <asm/range-protect.h> ++ ++/* ++ * restore_context() ++ * Restore the full context from sp (struct pt_reg *) ++ * ++ * Note: Recovered PC and CSR are saved on the stack and are to be popped off ++ * before returning. ++ */ ++.macro restore_context ++ move.4 a0, sp ; Set up a0 as base address for register recovery ++ move.4 sp, PT_SP(a0) ; Recover Stack pointer from save area ++ move.4 -4(sp)++, PT_PC(a0) ; Recover saved PC and save to stack ++ move.4 -4(sp)++, PT_CSR(a0) ; Recover saved csr and save to stack ++ move.4 d0, PT_D0(a0) ++ move.4 d1, PT_D1(a0) ++ move.4 d2, PT_D2(a0) ++ move.4 d3, PT_D3(a0) ++ move.4 d4, PT_D4(a0) ++ move.4 d5, PT_D5(a0) ++ move.4 d6, PT_D6(a0) ++ move.4 d7, PT_D7(a0) ++ move.4 d8, PT_D8(a0) ++ move.4 d9, PT_D9(a0) ++ move.4 d10, PT_D10(a0) ++ move.4 d11, PT_D11(a0) ++ move.4 d12, PT_D12(a0) ++ move.4 d13, PT_D13(a0) ++ move.4 d14, PT_D14(a0) ++ move.4 d15, PT_D15(a0) ++ move.4 a1, PT_A1(a0) ++ move.4 a2, PT_A2(a0) ++ move.4 a3, PT_A3(a0) ++ move.4 a4, PT_A4(a0) ++ move.4 a5, PT_A5(a0) ++ move.4 a6, PT_A6(a0) ++ move.4 acc0_hi, PT_ACC0HI(a0) ++ move.4 acc0_lo, PT_ACC0LO(a0) ++ move.4 mac_rc16, PT_MAC_RC16(a0) ++ move.4 acc1_hi, PT_ACC1HI(a0) ++ move.4 acc1_lo, PT_ACC1LO(a0) ++ move.4 source3, PT_SOURCE3(a0) ++ move.4 int_mask0, PT_INT_MASK0(a0) ++ move.4 int_mask1, PT_INT_MASK1(a0) ++ move.4 a0, PT_A0(a0) ++.endm ++ ++/* ++ * ldsr_thread_enable_interrupts() ++ * An assembly version of the enable interrupts function. ++ * ++ * The stack is fair game but all registers MUST be preserved. ++ * ++ */ ++.macro ldsr_thread_enable_interrupts ++ move.4 -4(sp)++, d3 ; Push d3 ++ move.4 -4(sp)++, a3 ; Push a3 ++ ++ /* ++ * Read the ROSR and obtain ~(1 << tid) ++ */ ++ lsr.4 d3, rosr, #0x2 ; Move the thread portion of ROSR into d3 ++ lsl.4 d3, #1, d3 ; perform a (1 << tid) ++ not.4 d3, d3 ; Negate the value of d3 == ~(1 << threadid) ++ ++ /* ++ * Get the value of the ldsr_soft_irq_mask ++ */ ++ moveai a3, #%hi(ldsr_soft_irq_mask) ++ move.4 a3, %lo(ldsr_soft_irq_mask)(a3) ++ ++ /* ++ * Now re-enable interrupts for this thread and then ++ * wakeup the LDSR. ++ */ ++ and.4 scratchpad1, scratchpad1, d3 ++ move.4 int_set0, a3 ++ ++ /* ++ * Restore the registers. ++ */ ++ move.4 a3, (sp)4++ ++ move.4 d3, (sp)4++ ++.endm ++ ++/* ++ * ret_from_interrupt_to_kernel() ++ * RFI function that is where do_IRQ() returns to if the thread was in kernel space. ++ */ ++ .section .text.ret_from_interrupt_to_kernel ++ .global ret_from_interrupt_to_kernel ++ret_from_interrupt_to_kernel: ++ atomic_lock_acquire ; Enter critical section ++ restore_context ; Restore the thread context ++ atomic_lock_release ; Leave critical section ++ ldsr_thread_enable_interrupts ; enable the threads interrupts ++ move.4 csr, (sp)4++ ; Restore csr from the stack ++ ret (sp)4++ ++ ++/* ++ * ret_from_interrupt_to_user() ++ * RFI function that is where do_IRQ() returns to if the thread was in user space. ++ * ++ * TODO: Do we really need the ciritical section handling in this code? ++ * ++ */ ++ .section .ret_from_interrupt_to_user.text ++ .global ret_from_interrupt_to_user ++ret_from_interrupt_to_user: ++ ldsr_thread_enable_interrupts ; enable the threads interrupts ++ movei d0, #(~(ASM_THREAD_SIZE-1)) ++ and.4 a3, sp, d0 ; a3 now has the thread info pointer ++ ++ /* ++ * Test if the scheduler needs to be called. ++ */ ++ btst TI_FLAGS(a3), #ASM_TIF_NEED_RESCHED ++ jmpeq.t 2f ++ moveai a5, #%hi(schedule) ++ calli a5, %lo(schedule)(a5) ; Call the scheduler. I will come back here. ++ ++ /* ++ * See if we have pending signals and call do_signal ++ * if needed. ++ */ ++2: atomic_lock_acquire ; Enter critical section ++ move.4 -4(sp)++, a1 ; Save A1 on the stack. We are going to use a1 it. ++ movei d0, #(~(ASM_THREAD_SIZE-1)) ++ and.4 a1, sp, d0 ; a1 now has the thread info pointer ++ btst TI_FLAGS(a1), #ASM_TIF_SIGPENDING ; Any signals needed? ++ jmpeq.t 1f ++ ++ ++ /* ++ * Now call do_signal() ++ */ ++ atomic_lock_release ; Leave critical section ++ move.4 d0, #0 ; oldset pointer is NULL ++ lea.1 d1, 4(sp) ; d1 is the regs pointer ++ moveai a5, #%hi(do_signal) ++ calli a5, %lo(do_signal)(a5) ; Call do_signal() ++ ++ /* ++ * Back from do_signal(), re-enter critical section. ++ */ ++ atomic_lock_acquire ; Enter critical section ++ ++1: move.4 a1, (sp)4++ ; pop A1 off the stack ++ ++ disable_kernel_ranges_for_current d15 ; disable kernel ranges ++ ++ restore_context ; restore the previous context ++ atomic_lock_release ; Leave critical section ++ move.4 csr, (sp)4++ ; Restore csr from the stack ++ ret (sp)4++ ++ ++/* ++ * restore_all_registers() ++ * ++ * restore_all_registers will be the alternate exit route for ++ * preempted processes that have called a signal handler ++ * and are returning back to user space. ++ */ ++ .global restore_all_registers ++restore_all_registers: ++ atomic_lock_acquire ; Enter critical section ++ disable_kernel_ranges_for_current d15 ; disable kernel ranges ++ atomic_lock_release ; Leave critical section ++ restore_context ; restore previous context ++ move.4 csr, (sp)4++ ; Restore csr from the stack ++ ret (sp)4++ ++ ++/* ++ * ret_from_fork() ++ * Called on the child's return from fork system call. ++ */ ++ .section .ret_from_fork.text ++ .global ret_from_fork ++ret_from_fork: ++ ;;; d0 contains the arg for schedule_tail ++ ;;; the others we don't care about as they are in PT_REGS (sp) ++ moveai a5, #%hi(schedule_tail) ++ calli a5, %lo(schedule_tail)(a5) ++ ++ atomic_lock_acquire ; Enter critical section ++ ++ move.4 a3, sp ++ move.4 d0, PT_D0(a3) ; Restore D0 ++ move.4 d1, PT_D1(a3) ; Restore D1 ++ move.4 d2, PT_D2(a3) ; Restore D2 ++ move.4 d3, PT_D3(a3) ; Restore D3 ++ move.4 d10, PT_D10(a3) ; Restore D10 ++ move.4 d11, PT_D11(a3) ; Restore D11 ++ move.4 d12, PT_D12(a3) ; Restore D12 ++ move.4 d13, PT_D13(a3) ; Restore D13 ++ move.4 a1, PT_A1(a3) ; Restore A1 ++ move.4 a2, PT_A2(a3) ; Restore A2 ++ move.4 a5, PT_A5(a3) ; Restore A5 ++ move.4 a6, PT_A6(a3) ; Restore A6 ++ move.4 sp, PT_SP(a3) ; Restore sp ++ move.4 a4, PT_PC(a3) ; Restore pc in register a4 ++ move.4 PT_FRAME_TYPE(a3), #0 ; Clear frame_type to indicate it is invalid. ++ ++ disable_kernel_ranges_for_current d15 ++ atomic_lock_release ; Leave critical section ++ calli a4, 0(a4) ; Return. ++ ++/* ++ * __switch_to() ++ * ++ * Call with: ++ * void *__switch_to(struct task_struct *prev, struct thread_struct *prev_switch, ++ * struct thread_struct *next_switch) ++ */ ++ .global __switch_to ++__switch_to: ++ ++ /* ++ * Set up register a3 to point to save area. ++ */ ++ movea a3, d1 ; a3 now holds prev_switch ++ move.4 (a3)4++, d10 ++ move.4 (a3)4++, d11 ++ move.4 (a3)4++, d12 ++ move.4 (a3)4++, d13 ++ move.4 (a3)4++, a1 ++ move.4 (a3)4++, a2 ++ move.4 (a3)4++, a5 ++ move.4 (a3)4++, a6 ++ move.4 (a3)4++, a7 ++ ++ /* ++ * Set up register a3 to point to restore area. ++ */ ++ movea a3, d2 ; a3 now holds next_switch ++ move.4 d10 , (a3)4++ ++ move.4 d11 , (a3)4++ ++ move.4 d12 , (a3)4++ ++ move.4 d13 , (a3)4++ ++ move.4 a1 , (a3)4++ ++ move.4 a2 , (a3)4++ ++ move.4 a5 , (a3)4++ ++ move.4 a6 , (a3)4++ ++ move.4 a7 , (a3)4++ ++ ++ /* ++ * Load the sw_ksp with the proper thread_info pointer. ++ */ ++ movei d15, #(~(ASM_THREAD_SIZE-1)) ++ and.4 a3, sp, d15 ; a3 now has the thread info pointer ++ moveai a4, #%hi(sw_ksp) ++ lea.1 a4, %lo(sw_ksp)(a4) ; a4 now has the base address of sw_ksp array ++ lsr.4 d15, ROSR, #2 ; Thread number + garbage ++ and.4 d15, #31, D15 ; Mask to get thread number into register D15 ++ move.4 (a4, d15), a3 ; Load the thread info pointer into the hw_ksp array.. ++ ++ /* ++ * We are done with context switch. Time to return.. ++ */ ++ ret a5 ++ ++ ++/* ++ * ubicom32_emulate_insn() ++ * Emulates the instruction. ++ * ++ * Call with: ++ * unsigned int ubicom32_emulate_insn(int source1, int source2, int source3, int *save_acc, int *save_csr); ++ */ ++ .global ubicom32_emulate_insn ++ .global trap_emulate ++ubicom32_emulate_insn: ++ movea a3, d3 ; a3 holds save_acc pointer ++ movea a4, d4 ; a4 hods save_csr pointer ++ move.4 source3, d2 ++ move.4 acc0_lo, (a3) ++ move.4 acc0_hi, 4(a3) ++ move.4 acc1_lo, 8(a3) ++ move.4 acc1_hi, 12(a3) ++ move.4 mac_rc16, 16(a3) ++ move.4 CSR, (a4) ++ setcsr_flush 0 ++ ++trap_emulate: ++ move.4 d0, d1 ++ setcsr_flush 0 ++ move.4 (a4), CSR ; Save csr ++ move.4 (a3), acc0_lo ++ move.4 4(a3), acc0_hi ++ move.4 8(a3), acc1_lo ++ move.4 12(a3), acc1_hi ++ move.4 16(a3), mac_rc16 ++ ret a5 +--- /dev/null ++++ b/arch/ubicom32/kernel/ubicom32_ksyms.c +@@ -0,0 +1,95 @@ ++/* ++ * arch/ubicom32/kernel/ubicom32_ksyms.c ++ * Ubicom32 architecture compiler support and misc symbols. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/linkage.h> ++#include <linux/sched.h> ++#include <linux/string.h> ++#include <linux/mm.h> ++#include <linux/user.h> ++#include <linux/elfcore.h> ++#include <linux/in6.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/semaphore.h> ++ ++#include <asm/setup.h> ++#include <asm/machdep.h> ++#include <asm/pgalloc.h> ++#include <asm/irq.h> ++#include <asm/checksum.h> ++#include <asm/current.h> ++ ++/* platform dependent support */ ++ ++EXPORT_SYMBOL(__ioremap); ++EXPORT_SYMBOL(iounmap); ++ ++EXPORT_SYMBOL(ip_fast_csum); ++ ++ ++/* Networking helper routines. */ ++EXPORT_SYMBOL(csum_partial_copy_nocheck); ++ ++/* The following are special because they're not called ++ explicitly (the C compiler generates them). Fortunately, ++ their interface isn't gonna change any time soon now, so ++ it's OK to leave it out of version control. */ ++EXPORT_SYMBOL(memcpy); ++EXPORT_SYMBOL(memset); ++ ++#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4 ++/* ++ * libgcc functions - functions that are used internally by the ++ * compiler... (prototypes are not correct though, but that ++ * doesn't really matter since they're not versioned). ++ */ ++extern void __ashldi3(void); ++extern void __ashrdi3(void); ++extern void __divsi3(void); ++extern void __lshrdi3(void); ++extern void __modsi3(void); ++extern void __muldi3(void); ++extern void __udivsi3(void); ++extern void __umodsi3(void); ++ ++/* gcc lib functions */ ++EXPORT_SYMBOL(__ashldi3); ++EXPORT_SYMBOL(__ashrdi3); ++EXPORT_SYMBOL(__divsi3); ++EXPORT_SYMBOL(__lshrdi3); ++EXPORT_SYMBOL(__modsi3); ++EXPORT_SYMBOL(__muldi3); ++EXPORT_SYMBOL(__udivsi3); ++EXPORT_SYMBOL(__umodsi3); ++#else ++extern void __libgcc_udivmodsi(void); ++extern void __libgcc_divmodsi(void); ++ ++EXPORT_SYMBOL(__libgcc_udivmodsi); ++EXPORT_SYMBOL(__libgcc_divmodsi); ++#endif +--- /dev/null ++++ b/arch/ubicom32/kernel/ubicom32_syscall.S +@@ -0,0 +1,643 @@ ++/* ++ * arch/ubicom32/kernel/ubicom32_syscall.S ++ * <TODO: Replace with short file description> ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/sys.h> ++#include <linux/linkage.h> ++#include <linux/unistd.h> ++ ++#include <asm/ubicom32-common.h> ++#include <asm/thread_info.h> ++#include <asm/asm-offsets.h> ++#include <asm/range-protect.h> ++ ++/* ++ * system_call() ++ */ ++ .section .syscall_entry.text ++ .global system_call ++system_call: ++ /* ++ * Regular ABI rules for function calls apply for syscall. d8 holds ++ * the syscall number. We will use that to index into the syscall table. ++ * d0 - d5 hold the parameters. ++ * ++ * First we get the current thread_info and swap to the kernel stack. ++ * This is done by reading the current thread and looking up the ksp ++ * from the sw_ksp array and storing it in a3. ++ * ++ * Then we reserve space for the syscall context a struct pt_regs and ++ * save it using a4 initially and later as sp. ++ * Once sp is set to the kernel sp we can leave the critical section. ++ * ++ * For the user case the kernel stack will have the following layout. ++ * ++ * a3 ksp[0] +-----------------------+ ++ * | Thread info area | ++ * | struct thread_info | ++ * +-----------------------+ ++ * : : ++ * | Kernel Stack Area | ++ * | | ++ * a4 / sp >>> +-----------------------+ ++ * | Context save area | ++ * | struct pt_reg | ++ * ksp[THREAD_SIZE-8] +-----------------------+ ++ * | 8 Byte Buffer Zone | ++ * ksp[THREAD_SIZE] +-----------------------+ ++ ++ * ++ * For kernel syscalls the layout is as follows. ++ * ++ * a3 ksp[0] +-----------------------+ ++ * | Thread info area | ++ * | struct thread_info | ++ * +-----------------------+ ++ * : : ++ * | Kernel Stack Area | ++ * | | ++ * a4 / sp >>> +-----------------------+ ++ * | Context save area | ++ * | struct pt_reg | ++ * sp at syscall entry +-----------------------+ ++ * | Callers Kernel Stack | ++ * : : ++ * ++ * Once the context is saved we optionally call syscall_trace and setup ++ * the exit routine and jump to the syscall. ++ */ ++ ++ /* ++ * load the base address for sw_ksp into a3 ++ * Note.. we cannot access it just yet as protection is still on. ++ */ ++ moveai a3, #%hi(sw_ksp) ++ lea.1 a3, %lo(sw_ksp)(a3) ++ ++ /* ++ * Enter critical section . ++ * ++ * The 'critical' aspects here are the switching the to the ksp and ++ * changing the protection registers, these both use per thread ++ * information so we need to protect from a context switch. For now this ++ * is done using the global atomic lock. ++ */ ++ atomic_lock_acquire ++ ++ thread_get_self d15 ; Load current thread number ++#ifdef CONFIG_PROTECT_KERNEL ++ lsl.4 d9, #1, d15 ; Convert to thread bit ++ enable_kernel_ranges d9 ++#endif ++ /* ++ * We need to Determine if this is a kernel syscall or user syscall. ++ * Start by loading the pointer for the thread_info structure for the ++ * current process in to a3. ++ */ ++ move.4 a3, (a3, d15) ; a3 = sw_ksp[d15] ++ ++ /* ++ * Now if this is a kernel thread the same value can be a acheived by ++ * masking off the lower bits on the current stack pointer. ++ */ ++ movei d9, #(~(ASM_THREAD_SIZE-1)) ; load mask ++ and.4 d9, sp, d9 ; apply mask ++ ++ /* ++ * d9 now has the masked version of the sp. If this is identical to ++ * what is in a3 then don't switch to ksp as we are already in the ++ * kernel. ++ */ ++ sub.4 #0, a3, d9 ++ ++ /* ++ * if d9 and a3 are not equal. We are usespace and have to shift to ++ * ksp. ++ */ ++ jmpne.t 1f ++ ++ /* ++ * Kernel Syscall. ++ * ++ * The kernel has called this routine. We have to pdec space for pt_regs ++ * from sp. ++ */ ++ pdec a4, PT_SIZE(sp) ; a4 = ksp - PT_SIZE ++ jmpt.t 2f ++ ++ /* ++ * Userspace Syscall. ++ * ++ * Add THREAD_SIZE and subtract PT_SIZE to create the proper ksp ++ */ ++1: movei d15, #(ASM_THREAD_SIZE - 8 - PT_SIZE) ++ lea.1 a4, (a3, d15) ; a4 = ksp + d15 ++ ++ /* ++ * Replace user stack pointer with kernel stack pointer (a4) ++ * Load -1 into frame_type in save area to indicate this is system call ++ * frame. ++ */ ++2: move.4 PT_A7(a4), a7 ; Save old sp/A7 on kernel stack ++ move.4 PT_FRAME_TYPE(a4), #-1 ; Set the frame type. ++ move.4 sp, a4 ; Change to ksp. ++ /* ++ * We are now officially back in the kernel! ++ */ ++ ++ /* ++ * Now that we are on the ksp we can leave the critical section ++ */ ++ atomic_lock_release ++ ++ /* ++ * We need to save a0 because we need to be able to restore it in ++ * the event that we need to handle a signal. It's not generally ++ * a callee-saved register but is the GOT pointer. ++ */ ++ move.4 PT_A0(sp), a0 ; Save A0 on kernel stack ++ ++ /* ++ * We still need to save d10-d13, a1, a2, a5, a6 in the kernel frame ++ * for this process, we also save the system call params in the case of ++ * syscall restart. (note a7 was saved above) ++ */ ++ move.4 PT_A1(sp), a1 ; Save A1 on kernel stack ++ move.4 PT_A2(sp), a2 ; Save A2 on kernel stack ++ move.4 PT_A5(sp), a5 ; Save A5 on kernel stack ++ move.4 PT_A6(sp), a6 ; Save A6 on kernel stack ++ move.4 PT_PC(sp), a5 ; Save A5 at the PC location ++ move.4 PT_D10(sp), d10 ; Save D10 on kernel stack ++ move.4 PT_D11(sp), d11 ; Save D11 on kernel stack ++ move.4 PT_D12(sp), d12 ; Save D12 on kernel stack ++ move.4 PT_D13(sp), d13 ; Save D13 on kernel stack ++ ++ /* ++ * Now save the syscall parameters ++ */ ++ move.4 PT_D0(sp), d0 ; Save d0 on kernel stack ++ move.4 PT_ORIGINAL_D0(sp), d0 ; Save d0 on kernel stack ++ move.4 PT_D1(sp), d1 ; Save d1 on kernel stack ++ move.4 PT_D2(sp), d2 ; Save d2 on kernel stack ++ move.4 PT_D3(sp), d3 ; Save d3 on kernel stack ++ move.4 PT_D4(sp), d4 ; Save d4 on kernel stack ++ move.4 PT_D5(sp), d5 ; Save d5 on kernel stack ++ /* add this back if we ever have a syscall with 7 args */ ++ move.4 PT_D8(sp), d8 ; Save d8 on kernel stack ++ ++ /* ++ * Test if syscalls are being traced and if they are jump to syscall ++ * trace (it will comeback here) ++ */ ++ btst TI_FLAGS(a3), #ASM_TIF_SYSCALL_TRACE ++ jmpne.f .Lsystem_call__trace ++.Lsystem_call__trace_complete: ++ /* ++ * Check for a valid call number [ 0 <= syscall_number < NR_syscalls ] ++ */ ++ cmpi d8, #0 ++ jmplt.f 3f ++ cmpi d8, #NR_syscalls ++ jmplt.t 4f ++ ++ /* ++ * They have passed an invalid number. Call sys_ni_syscall staring by ++ * load a4 with the base address of sys_ni_syscall ++ */ ++3: moveai a4, #%hi(sys_ni_syscall) ++ lea.1 a4, %lo(sys_ni_syscall)(a4) ++ jmpt.t 5f ; Jump to regular processing ++ ++ /* ++ * Validated syscall, load the syscall table base address into a3 and ++ * read the syscall ptr out. ++ */ ++4: moveai a3, #%hi(sys_call_table) ++ lea.1 a3, %lo(sys_call_table)(a3) ; a3 = sys_call_table ++ move.4 a4, (a3, d8) ; a4 = sys_call_table[d8] ++ ++ /* ++ * Before calling the syscall, setup a5 so that syscall_exit is called ++ * on return from syscall ++ */ ++5: moveai a5, #%hi(syscall_exit) ; Setup return address ++ lea.1 a5, %lo(syscall_exit)(a5) ; from system call ++ ++ /* ++ * If the syscall is __NR_rt_rigreturn then we have to test d1 to ++ * figure out if we have to change change the return routine to restore ++ * all registers. ++ */ ++ cmpi d8, #__NR_rt_sigreturn ++ jmpeq.f 6f ++ ++ /* ++ * Launch system call (it will return through a5 - syscall_exit) ++ */ ++ calli a3, 0(a4) ++ ++ /* ++ * System call is rt_sigreturn. Test d1. If it is 1 we have to ++ * change the return address to restore_all_registers ++ */ ++6: cmpi d1, #1 ++ jmpne.t 7f ++ ++ moveai a5, #%hi(restore_all_registers) ; Setup return address ++ lea.1 a5, %lo(restore_all_registers)(a5) ; to restore_all_registers. ++ ++ /* ++ * Launch system call (it will return through a5) ++ */ ++7: calli a3, 0(a4) ; Launch system call ++ ++.Lsystem_call__trace: ++ /* ++ * Syscalls are being traced. ++ * Call syscall_trace, (return here) ++ */ ++ moveai a4, #%hi(syscall_trace) ++ calli a5, %lo(syscall_trace)(a4) ++ ++ /* ++ * Restore syscall state (it would have been discarded during the ++ * syscall trace) ++ */ ++ move.4 d0, PT_D0(sp) ; Restore d0 from kernel stack ++ move.4 d1, PT_D1(sp) ; Restore d1 from kernel stack ++ move.4 d2, PT_D2(sp) ; Restore d2 from kernel stack ++ move.4 d3, PT_D3(sp) ; Restore d3 from kernel stack ++ move.4 d4, PT_D4(sp) ; Restore d4 from kernel stack ++ move.4 d5, PT_D5(sp) ; Restore d5 from kernel stack ++ /* add this back if we ever have a syscall with 7 args */ ++ move.4 d8, PT_D8(sp) ; Restore d8 from kernel stack ++ ++ /* ++ * return to syscall ++ */ ++ jmpt.t .Lsystem_call__trace_complete ++ .size system_call, . - system_call ++ ++/* ++ * syscall_exit() ++ */ ++ .section .syscall_exit.text ++ .global syscall_exit ++syscall_exit: ++ /* ++ * d0 contains the return value. We should move that into the kernel ++ * stack d0 location. We will be transitioning from kernel to user ++ * mode. Test the flags and see if we have to call schedule. If we are ++ * going to truly exit then all that has to be done is that from the ++ * kernel stack we have to restore d0, a0, a1, a2, a5, a6 and sp (a7)bb ++ * and then return via a5. ++ */ ++ ++ /* ++ * Save d0 to pt_regs ++ */ ++ move.4 PT_D0(sp), d0 ; Save d0 into the kernel stack ++ ++ /* ++ * load the thread_info structure by masking off the THREAD_SIZE ++ * bits. ++ * ++ * Note: we used to push a1, but now we don't as we are going ++ * to eventually restore it to the userspace a1. ++ */ ++ movei d9, #(~(ASM_THREAD_SIZE-1)) ++ and.4 a1, sp, d9 ++ ++ /* ++ * Are any interesting bits set on TI flags, if there are jump ++ * aside to post_processing. ++ */ ++ move.4 d9, #(_TIF_SYSCALL_TRACE | _TIF_NEED_RESCHED | _TIF_SIGPENDING) ++ and.4 #0, TI_FLAGS(a1), d9 ++ jmpne.f .Lsyscall_exit__post_processing ; jump to handler ++.Lsyscall_exit__post_processing_complete: ++ ++ move.4 d0, PT_D0(sp) ; Restore D0 from kernel stack ++ move.4 d1, PT_D1(sp) ; Restore d1 from kernel stack ++ move.4 d2, PT_D2(sp) ; Restore d2 from kernel stack ++ move.4 d3, PT_D3(sp) ; Restore d3 from kernel stack ++ move.4 d4, PT_D4(sp) ; Restore d4 from kernel stack ++ move.4 d5, PT_D5(sp) ; Restore d5 from kernel stack ++ move.4 d8, PT_D8(sp) ; Restore d8 from kernel stack ++ move.4 d10, PT_D10(sp) ; Restore d10 from kernel stack ++ move.4 d11, PT_D11(sp) ; Restore d11 from kernel stack ++ move.4 d12, PT_D12(sp) ; Restore d12 from kernel stack ++ move.4 d13, PT_D13(sp) ; Restore d13 from kernel stack ++ move.4 a1, PT_A1(sp) ; Restore A1 from kernel stack ++ move.4 a2, PT_A2(sp) ; Restore A2 from kernel stack ++ move.4 a5, PT_A5(sp) ; Restore A5 from kernel stack ++ move.4 a6, PT_A6(sp) ; Restore A6 from kernel stack ++ move.4 a0, PT_A0(sp) ; Restore A6 from kernel stack ++ ++ /* ++ * this is only for debug, and could be removed for production builds ++ */ ++ move.4 PT_FRAME_TYPE(sp), #0 ; invalidate frame_type ++ ++#ifdef CONFIG_PROTECT_KERNEL ++ /* ++ * Enter critical section ++ */ ++ atomic_lock_acquire ++ disable_kernel_ranges_for_current d15 ++#endif ++ /* ++ * Lastly restore userspace stack ptr ++ * ++ * Note: that when protection is on we need to hold the lock around the ++ * stack swap as well because otherwise the protection could get ++ * inadvertently disabled again at the end of a context switch. ++ */ ++ move.4 a7, PT_A7(sp) ; Restore A7 from kernel stack ++ ++ /* ++ * We are now officially back in userspace! ++ */ ++ ++#ifdef CONFIG_PROTECT_KERNEL ++ /* ++ * Leave critical section and return to user space. ++ */ ++ atomic_lock_release ++#endif ++ calli a5, 0(a5) ; Back to userspace code. ++ ++ bkpt #-1 ; we will never get here ++ ++ /* ++ * Post syscall processing. (unlikely part of syscall_exit) ++ * ++ * Are we tracing syscalls. If TIF_SYSCALL_TRACE is set, call ++ * syscall_trace routine and return here. ++ */ ++.Lsyscall_exit__post_processing: ++ btst TI_FLAGS(a1), #ASM_TIF_SYSCALL_TRACE ++ jmpeq.t 1f ++ moveai a5, #%hi(syscall_trace) ++ calli a5, %lo(syscall_trace)(a5) ++ ++ /* ++ * Do we need to resched ie call schedule. If TIF_NEED_RESCHED is set, ++ * call the scheduler, it will come back here. ++ */ ++1: btst TI_FLAGS(a1), #ASM_TIF_NEED_RESCHED ++ jmpeq.t 2f ++ moveai a5, #%hi(schedule) ++ calli a5, %lo(schedule)(a5) ++ ++ /* ++ * Do we need to post a signal, if TIF_SIGPENDING is set call the ++ * do_signal. ++ */ ++2: btst TI_FLAGS(a1), #ASM_TIF_SIGPENDING ++ jmpeq.t .Lsyscall_exit__post_processing_complete ++ ++ /* ++ * setup the do signal call ++ */ ++ move.4 d0, #0 ; oldset pointer is NULL ++ lea.1 d1, (sp) ; d1 is the regs pointer. ++ moveai a5, #%hi(do_signal) ; Have to call do_signal ++ calli a5, %lo(do_signal)(a5) ++ ++ jmpt.t .Lsyscall_exit__post_processing_complete ++ ++ .size syscall_exit, . - syscall_exit ++ ++/* ++ * kernel_execve() ++ * kernel_execv is called when we the kernel is starting a ++ * userspace application. ++ */ ++ .section .kernel_unprotected ++ .global kernel_execve ++kernel_execve: ++ move.4 -4(sp)++, a5 ; Save return address ++ /* ++ * Call execve ++ */ ++ movei d8, #__NR_execve ; call execve ++ moveai a3, #%hi(system_call) ++ calli a5, %lo(system_call)(a3) ++ move.4 a5, (sp)4++ ++ ++ /* ++ * protection was enabled again at syscall exit, but we want ++ * to return to kernel so we enable it again. ++ */ ++#ifdef CONFIG_PROTECT_KERNEL ++ /* ++ * We are entering the kernel so we need to disable the protection. ++ * Enter critical section, disable ranges and leave critical section. ++ */ ++ atomic_lock_acquire ; Enter critical section ++ enable_kernel_ranges_for_current d15 ++ atomic_lock_release ; Leave critical section ++#endif ++ ret a5 ; jump back to the kernel ++ ++ .size kernel_execve, . - kernel_execve ++ ++/* ++ * signal_trampoline() ++ * ++ * Deals with transitioning from to userspace signal handlers and returning ++ * to userspace, only called from the kernel. ++ * ++ */ ++ .section .kernel_unprotected ++ .global signal_trampoline ++signal_trampoline: ++ /* ++ * signal_trampoline is called when we are jumping from the kernel to ++ * the userspace signal handler. ++ * ++ * The following registers are relevant. (set setup_rt_frame) ++ * sp is the user space stack not the kernel stack ++ * d0 = signal number ++ * d1 = siginfo_t * ++ * d2 = ucontext * ++ * d3 = the user space signal handler ++ * a0 is set to the GOT if userspace application is FDPIC, otherwise 0 ++ * a3 is set to the FD for the signal if userspace application is FDPIC ++ */ ++#ifdef CONFIG_PROTECT_KERNEL ++ /* ++ * We are leaving the kernel so we need to enable the protection. ++ * Enter critical section, disable ranges and leave critical section. ++ */ ++ atomic_lock_acquire ; Enter critical section ++ disable_kernel_ranges_for_current d15 ; disable kernel ranges ++ atomic_lock_release ; Leave critical section ++#endif ++ /* ++ * The signal handler pointer is in register d3 so tranfer it to a4 and ++ * call it ++ */ ++ movea a4, d3 ; signal handler ++ calli a5, 0(a4) ++ ++ /* ++ * Return to userspace through rt_syscall which is stored on top of the ++ * stack d1 contains ret_via_interrupt status. ++ */ ++ move.4 d8, (sp) ; d8 (syscall #) = rt_syscall ++ move.4 d1, 4(sp) ; d1 = ret_via_interrupt ++ moveai a3, #%hi(system_call) ; call system_call ++ calli a5, %lo(system_call)(a3) ++ ++ bkpt -1 ; will never get here. ++ .size signal_trampoline, . - signal_trampoline ++ ++/* ++ * kernel_thread_helper() ++ * ++ * Entry point for kernel threads (only referenced by kernel_thread()). ++ * ++ * On execution d0 will be 0, d1 will be the argument to be passed to the ++ * kernel function. ++ * d2 contains the kernel function that needs to get called. ++ * d3 will contain address to do_exit which needs to get moved into a5. ++ * ++ * On return from fork the child thread d0 will be 0. We call this dummy ++ * function which in turn loads the argument ++ */ ++ .section .kernel_unprotected ++ .global kernel_thread_helper ++kernel_thread_helper: ++ /* ++ * Create a kernel thread. This is called from ret_from_vfork (a ++ * userspace return routine) so we need to put it in an unprotected ++ * section and re-enable protection before calling the vector in d2. ++ */ ++ ++#ifdef CONFIG_PROTECT_KERNEL ++ /* ++ * We are entering the kernel so we need to disable the protection. ++ * Enter critical section, disable ranges and leave critical section. ++ */ ++ atomic_lock_acquire ; Enter critical section ++ enable_kernel_ranges_for_current d0 ++ atomic_lock_release ; Leave critical section ++#endif ++ /* ++ * Move argument for kernel function into d0, and set a5 return address ++ * (a5) to do_exit and return through a2 ++ */ ++ move.4 d0, d1 ; d0 = arg ++ move.4 a5, d3 ; a5 = do_exit ++ ret d2 ; call function ptr in d2 ++ ++ .size kernel_thread_helper, . - kernel_thread_helper ++ ++/* ++ * execve_intercept() ++ */ ++ .section .text ++ .global execve_intercept ++execve_intercept: ++ move.4 d3, sp ; Save retrun address ++ moveai a3, #%hi(sys_execve) ++ calli a3, %lo(sys_execve)(a3) ++ ++ .size execve_intercept, . - execve_intercept ++ ++/* ++ * vfork_intercept() ++ */ ++ .section .text ++ .global vfork_intercept ++vfork_intercept: ++ move.4 d0, sp ; Save pt_regs address ++ moveai a3, #%hi(sys_vfork) ++ calli a3, %lo(sys_vfork)(a3) ++ ++ .size vfork_intercept, . - vfork_intercept ++ ++/* ++ * clone_intercept() ++ */ ++ .section .text ++ .global clone_intercept ++clone_intercept: ++ move.4 d2, sp ; Save pt_regs address ++ moveai a3, #%hi(sys_clone) ++ calli a3, %lo(sys_clone)(a3) ++ ++ .size clone_intercept, . - clone_intercept ++ ++/* ++ * sys_sigsuspend() ++ */ ++ .section .text ++ .global sys_sigsuspend ++sys_sigsuspend: ++ move.4 d0, sp ; Pass pointer to pt_regs in d0 ++ moveai a3, #%hi(do_sigsuspend) ++ calli a3, %lo(do_sigsuspend)(a3) ++ ++ .size sys_sigsuspend, . - sys_sigsuspend ++ ++/* ++ * sys_rt_sigsuspend() ++ */ ++ .section .text ++ .global sys_rt_sigsuspend ++sys_rt_sigsuspend: ++ move.4 d0, sp ; Pass pointer to pt_regs in d0 ++ moveai a3, #%hi(do_rt_sigsuspend) ++ calli a3, %lo(do_rt_sigsuspend)(a3) ++ ++ .size sys_rt_sigsuspend, . - sys_rt_sigsuspend ++ ++/* ++ * sys_rt_sigreturn() ++ */ ++ .section .text ++ .global sys_rt_sigreturn ++sys_rt_sigreturn: ++ move.4 d0, sp ; Pass pointer to pt_regs in d0 ++ moveai a3, #%hi(do_rt_sigreturn) ++ calli a3, %lo(do_rt_sigreturn)(a3) ++ ++ .size sys_rt_sigreturn, . - sys_rt_sigreturn ++ ++/* ++ * sys_sigaltstack() ++ */ ++ .section .text ++ .global sys_sigaltstack ++sys_sigaltstack: ++ move.4 d0, sp ; Pass pointer to pt_regs in d0 ++ moveai a3, #%hi(do_sys_sigaltstack) ++ calli a3, %lo(do_sys_sigaltstack)(a3) ++ ++ .size sys_sigaltstack, . - sys_sigaltstack +--- /dev/null ++++ b/arch/ubicom32/kernel/unaligned_trap.c +@@ -0,0 +1,698 @@ ++/* ++ * arch/ubicom32/kernel/unaligned_trap.c ++ * Handle unaligned traps in both user or kernel space. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <asm/cacheflush.h> ++#include <asm/traps.h> ++ ++#define FALSE 0 ++#define TRUE 1 ++ ++/* no possible trap */ ++#define UNUSED 0 ++/* possible source operand trap */ ++#define SRC 1 ++#define SRC_2 2 ++/* possible destination operand trap */ ++#define DEST 3 ++#define DEST_2 4 ++/* can be either source or destination or both */ ++#define TWO_OP 5 ++#define TWO_OP_2 6 ++ ++/* TODO: What is the real value here, put something in to make it compile for ++ * now */ ++#define MOVE_2 0x0d ++#define LSL_2 0x11 ++#define LSR_2 0x13 ++#define MOVEI 0x19 ++#define CMPI 0x18 ++ ++static int op_format[32] = ++{ ++ TWO_OP, /* 0x00 */ ++ UNUSED, ++ SRC, ++ UNUSED, ++ TWO_OP, /* 0x04 */ ++ TWO_OP, ++ SRC, ++ UNUSED, ++ TWO_OP_2, /* 0x08 */ ++ TWO_OP, ++ TWO_OP_2, ++ TWO_OP, ++ TWO_OP_2, /* 0x0C */ ++ TWO_OP, ++ TWO_OP_2, ++ TWO_OP, ++ TWO_OP, /* 0x10 */ ++ TWO_OP_2, ++ TWO_OP, ++ TWO_OP, ++ UNUSED, /* 0x14 */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ SRC_2, /* 0x18 */ ++ DEST_2, ++ UNUSED, ++ UNUSED, ++ UNUSED, /* 0x1C */ ++ UNUSED, ++ UNUSED, /* unaligned CALLI will not be fixed. */ ++ UNUSED ++}; ++ ++static int op_0_format[32] = ++{ ++ UNUSED, /* 0x00 */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ UNUSED, /* 0x04 - ret don't fix - bad ret is always wrong */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ UNUSED, /* 0x08 */ ++ UNUSED, ++ TWO_OP, ++ TWO_OP_2, ++ TWO_OP, /* 0x0c */ ++ TWO_OP_2, ++ TWO_OP, ++ UNUSED, /* .1 can't trap */ ++ UNUSED, /* 0x10 */ ++ UNUSED, ++ SRC, ++ UNUSED, ++ UNUSED, /* 0x14 */ ++ TWO_OP_2, ++ UNUSED, ++ UNUSED, ++ UNUSED, /* 0x18 */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ DEST, /* 0x1c */ ++ DEST, ++ DEST, ++ DEST, /* all lea have 32-bit destination */ ++}; ++ ++static int op_2_format[32] = ++{ ++ UNUSED, /* 0x00 */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ UNUSED, /* 0x04 */ ++ UNUSED, ++ SRC, ++ UNUSED, ++ UNUSED, /* 0x08 crcgen is .1 */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ UNUSED, /* 0x0c */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ SRC, /* 0x10 */ ++ SRC_2, ++ SRC, ++ SRC_2, ++ SRC, /* 0x14 */ ++ SRC_2, ++ SRC, ++ UNUSED, ++ UNUSED, /* 0x18 */ ++ UNUSED, ++ SRC, ++ UNUSED, ++ SRC, /* 0x1c */ ++ UNUSED, ++ SRC_2, ++ UNUSED, ++}; ++ ++static int op_6_format[32] = ++{ ++ SRC_2, /* 0x00 */ ++ SRC_2, ++ SRC_2, ++ SRC_2, ++ SRC_2, /* 0x04 */ ++ SRC_2, ++ UNUSED, ++ SRC_2, ++ SRC, /* 0x08 MULS.4 */ ++ SRC_2, ++ SRC, ++ UNUSED, ++ UNUSED, /* 0x0c */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ SRC, /* 0x10 */ ++ SRC_2, ++ SRC, ++ SRC_2, ++ UNUSED, /* 0x14 */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ UNUSED, /* 0x18 */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++ UNUSED, /* 0x1c */ ++ UNUSED, ++ UNUSED, ++ UNUSED, ++}; ++ ++/* ++ * unaligned_get_address() ++ * get an address using save_an and save_dn registers, and updates save_an ++ * with side effects ++ */ ++unsigned char *unaligned_get_address(int thread, int specifier, int four_byte, ++ unsigned int save_an[], ++ unsigned int save_dn[], int *write_back_an) ++{ ++ unsigned char *address; ++ ++ int areg = (specifier >> 5) & 7; ++ if ((specifier >> 8) == 2) { ++ int offset = specifier & 0xf; ++ offset = ((offset << 28) >> 28); ++ if (likely(four_byte)) { ++ offset <<= 2; ++ } else { ++ offset <<= 1; ++ } ++ if (specifier & 0x10) { ++ address = (unsigned char *)(save_an[areg] + offset); ++ } else { ++ address = (unsigned char *)save_an[areg]; ++ } ++ save_an[areg] = save_an[areg] + offset; ++ ++ /* ++ * Let caller know An registers have been modified. ++ */ ++ *write_back_an = 1; ++ } else if ((specifier >> 8) == 3) { ++ int dreg = specifier & 0xf; ++ if (likely(four_byte)) { ++ address = (unsigned char *)(save_an[areg] + ++ (save_dn[dreg] << 2)); ++ } else { ++ address = (unsigned char *)(save_an[areg] + ++ (save_dn[dreg] << 1)); ++ } ++ } else { ++ int offset = ((specifier >> 3) & 0x60) | (specifier & 0x1f); ++ if (likely(four_byte)) { ++ address = (unsigned char *)(save_an[areg] + ++ (offset << 2)); ++ } else { ++ address = (unsigned char *)(save_an[areg] + ++ (offset << 1)); ++ } ++ } ++ ++ return address; ++} ++ ++static int save_dn[16]; ++static int save_an[8]; ++static int save_acc[5]; ++ ++/* ++ * unaligned_emulate() ++ * emulate the instruction at thread's pc that has taken an unaligned data ++ * trap. ++ * ++ * source or destination or both might be unaligned ++ * the instruction must have a memory source or destination or both ++ * the emulated instruction is copied and executed in this thread ++ * ++ * TODO: Protection is handled outside of this function ++ * TODO: handling simultaneous unaligned and memory protection traps ++ * ++ * Get thread state ++ * the PC and instruction (and local copy, emulate_inst), and An ++ * and Dn registers ++ * All implicit soruce state (source3, CSR, accumulators) ++ ++ * if the instruction has a memory source ++ * Use the instruction, An and Dn registers to form src_address ++ * get unaligned source data from src_address (usually sign ++ * extended) ++ * (2 bytes, with or without sign extension, or 4 bytes) ++ * modify emulate_inst to use d0 as source ++ * else ++ * get the soure operand from one of thread's registers ++ * if instruction has a memory destination ++ * Use the instruction, An and Dn registers to form dest_address ++ * modify emulate_inst to use d0 as destination ++ * if there was a memory source ++ * put the source data in thread's d0 ++ * get the source-2 Dn operand and source 3 operand from thread ++ * execute modified inst ++ * (save it, flush caches, set up local values for implicit ++ * sources, execute, save explicit and implicit results) ++ * if inst has destination address ++ * copy result to dest_address, possibly unaligned, 1, 2, or 4 ++ * bytes ++ * restore thread's implicit results (modified address registers, CSR, ++ * accumulators) add 4 to thread's pc ++ */ ++void unaligned_emulate(unsigned int thread) ++{ ++ unsigned int pc; ++ unsigned int inst; ++ unsigned int op; ++ unsigned int subop; ++ int format; ++ unsigned int emulate_inst; ++ int four_byte; ++ int src_operand, dest_operand; ++ int save_csr; ++ int source3; ++ unsigned int source1; ++ unsigned int source_data; ++ unsigned char *dest_address = NULL; ++ int source2 = 0; ++ unsigned int result; ++ unsigned int write_back_an = 0; ++ unsigned int chip_id_copy; ++ ++ extern unsigned int trap_emulate; ++ extern unsigned int ubicom32_emulate_insn(int source1, int source2, ++ int source3, int *save_acc, ++ int *save_csr); ++ ++ /* ++ * get the chip_id ++ */ ++ asm volatile ( ++ " move.4 %0, chip_id \n\t" /* get chip_id. */ ++ : "=r"(chip_id_copy) ++ : ++ ); ++ ++ /* ++ * get the pc ++ */ ++ asm volatile ( ++ " move.4 CSR, %1 \n\t" /* set source thread in ++ * CSR */ ++ " setcsr_flush 0 \n\t" ++ " move.4 %0, pc \n\t" ++ " move.4 CSR, #0 \n\t" /* restore CSR */ ++ " setcsr_flush 0 \n\t" ++ : "=a"(pc) ++ : "d" ((1 << 8) | (thread << 9)) ++ : "cc" ++ ); ++ ++ inst = *((unsigned int *)pc); ++ op = inst >> 27; ++ if (unlikely(op == 2 || op == 6)) { ++ subop = (inst >> 21) & 0x1f; ++ } else { ++ subop = (inst >> 11) & 0x1f; ++ } ++ format = op_format[op]; ++ emulate_inst = inst; ++ ++ if (op == 0) { ++ format = op_0_format[subop]; ++ } else if (op == 2) { ++ format = op_2_format[subop]; ++ } else if (op == 6) { ++ format = op_6_format[subop]; ++ } ++ ++ if (unlikely(format == UNUSED)) { ++ /* ++ * We are not going to emulate this. Bump PC by 4 and move on. ++ */ ++ asm volatile ( ++ " move.4 CSR, %0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 pc, %1 \n\t" ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ : ++ : "d"((1 << 14) | (thread << 15)), "d"(pc + 4) ++ : "cc" ++ ); ++ return; ++ } ++ ++ four_byte = (format == TWO_OP || format == DEST || format == SRC); ++ ++ /* ++ * source or destination memory operand needs emulation ++ */ ++ src_operand = (format == SRC || ++ format == SRC_2 || ++ format == TWO_OP || ++ format == TWO_OP_2) && ++ ((inst >> 8) & 7) > 1; ++ ++ dest_operand = (format == DEST || ++ format == DEST_2 || ++ format == TWO_OP || ++ format == TWO_OP_2) && ++ ((inst >> 24) & 7) > 1; ++ ++ /* ++ * get thread's implicit sources (not covered by source context select). ++ * data and address registers and CSR (for flag bits) and src3 and ++ * accumulators ++ */ ++ asm volatile ( ++ " move.4 CSR, %2 \n\t" /* set source thread in ++ * CSR */ ++ " setcsr_flush 0 \n\t" ++ " move.4 (%3), d0 \n\t" /* get dn registers */ ++ " move.4 4(%3), d1 \n\t" ++ " move.4 8(%3), d2 \n\t" ++ " move.4 12(%3), d3 \n\t" ++ " move.4 16(%3), d4 \n\t" ++ " move.4 20(%3), d5 \n\t" ++ " move.4 24(%3), d6 \n\t" ++ " move.4 28(%3), d7 \n\t" ++ " move.4 32(%3), d8 \n\t" ++ " move.4 36(%3), d9 \n\t" ++ " move.4 40(%3), d10 \n\t" ++ " move.4 44(%3), d11 \n\t" ++ " move.4 48(%3), d12 \n\t" ++ " move.4 52(%3), d13 \n\t" ++ " move.4 56(%3), d14 \n\t" ++ " move.4 60(%3), d15 \n\t" ++ " move.4 (%4), a0 \n\t" /* get an registers */ ++ " move.4 4(%4), a1 \n\t" ++ " move.4 8(%4), a2 \n\t" ++ " move.4 12(%4), a3 \n\t" ++ " move.4 16(%4), a4 \n\t" ++ " move.4 20(%4), a5 \n\t" ++ " move.4 24(%4), a6 \n\t" ++ " move.4 28(%4), a7 \n\t" ++ " move.4 %0, CSR \n\t" /* get csr and source3 ++ * implicit operands */ ++ " move.4 %1, source3 \n\t" ++ " move.4 (%5), acc0_lo \n\t" /* get accumulators */ ++ " move.4 4(%5), acc0_hi \n\t" ++ " move.4 8(%5), acc1_lo \n\t" ++ " move.4 12(%5), acc1_hi \n\t" ++ " move.4 16(%5), mac_rc16 \n\t" ++ " move.4 CSR, #0 \n\t" /* restore CSR */ ++ " setcsr_flush 0 \n\t" ++ : "=m"(save_csr), "=m"(source3) ++ : "d"((1 << 8) | (thread << 9)), ++ "a"(save_dn), "a"(save_an), "a"(save_acc) ++ : "cc" ++ ); ++ ++ /* ++ * turn off thread select bits if they were on ++ */ ++ BUG_ON((save_csr & 0x04100) != 0); ++ if (unlikely(save_csr & 0x04100)) { ++ /* ++ * Things are in funny state as thread select bits are on in ++ * csr. PANIC. ++ */ ++ panic("In unaligned trap handler. Trap thread CSR has thread " ++ "select bits on.\n"); ++ } ++ ++ save_csr = save_csr & 0x1000ff; ++ ++ /* ++ * get the source1 operand ++ */ ++ source1 = 0; ++ if (src_operand) { ++ unsigned char *src_address; ++ ++ /* ++ * source1 comes from memory ++ */ ++ BUG_ON(!(format == TWO_OP || format == TWO_OP_2 || ++ format == SRC || format == SRC_2)); ++ src_address = unaligned_get_address(thread, inst & 0x7ff, ++ four_byte, save_an, ++ save_dn, &write_back_an); ++ ++ /* ++ * get data (possibly unaligned) ++ */ ++ if (likely(four_byte)) { ++ source_data = (*src_address << 24) | ++ (*(src_address + 1) << 16) | ++ (*(src_address + 2) << 8) | ++ *(src_address + 3); ++ source1 = source_data; ++ } else { ++ source1 = *src_address << 8 | ++ *(src_address + 1); ++ ++ /* ++ * Source is not extended if the instrution is MOVE.2 or ++ * if the cpu CHIP_ID >= 0x30000 and the instruction is ++ * either LSL.2 or LSR.2. All other cases have to be ++ * sign extended. ++ */ ++ if ((!(op == 2 && subop == MOVE_2)) && ++ (!((chip_id_copy >= 0x30000) && ++ (subop == LSL_2 || subop == LSR_2)))) { ++ /* ++ * Have to sign extend the .2 entry. ++ */ ++ source1 = ((unsigned int) ++ ((signed int) ++ ((signed short) source1))); ++ } ++ } ++ } else if (likely(op != MOVEI)) { ++ /* ++ * source1 comes from a register, using move.4 d0, src1 ++ * unaligned_emulate_get_source is pointer to code to insert remulated instruction ++ */ ++ extern unsigned int unaligned_emulate_get_src; ++ *((int *)&unaligned_emulate_get_src) &= ~(0x7ff); ++ *((int *)&unaligned_emulate_get_src) |= (inst & 0x7ff); ++ flush_dcache_range((unsigned long)(&unaligned_emulate_get_src), ++ (unsigned long)(&unaligned_emulate_get_src) + 4); ++ ++ asm volatile ( ++ /* source1 uses thread's registers */ ++ " move.4 CSR, %1 \n\t" ++ " setcsr_flush 0 \n\t" ++ "unaligned_emulate_get_src: \n\t" ++ " move.4 %0, #0 \n\t" ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ : "=d" (source1) ++ : "d" ((1 << 8) | (thread << 9)) ++ : "cc" ++ ); ++ } ++ ++ /* ++ * get the destination address ++ */ ++ if (dest_operand) { ++ BUG_ON(!(format == TWO_OP || format == TWO_OP_2 || ++ format == DEST || format == DEST_2)); ++ dest_address = unaligned_get_address(thread, ++ ((inst >> 16) & 0x7ff), ++ four_byte, save_an, ++ save_dn, &write_back_an); ++ } ++ ++ if (write_back_an) { ++ /* ++ * restore any modified An registers ++ */ ++ asm volatile ( ++ " move.4 CSR, %0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 a0, (%1) \n\t" ++ " move.4 a1, 4(%1) \n\t" ++ " move.4 a2, 8(%1) \n\t" ++ " move.4 a3, 12(%1) \n\t" ++ " move.4 a4, 16(%1) \n\t" ++ " move.4 a5, 20(%1) \n\t" ++ " move.4 a6, 24(%1) \n\t" ++ " move.4 a7, 28(%1) \n\t" ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ : ++ : "d" ((1 << 14) | (thread << 15)), "a" (save_an) ++ : "cc" ++ ); ++ } ++ ++ /* ++ * get source 2 register if needed, and modify inst to use d1 for ++ * source-2 source-2 will come from this thread, not the trapping thread ++ */ ++ source2 = 0; ++ if ((op >= 8 && op <= 0x17) || ++ ((op == 2 || op == 6) && (inst & 0x4000000))) { ++ int src_dn = (inst >> 11) & 0xf; ++ source2 = save_dn[src_dn]; ++ /* ++ * force the emulated instruction to use d1 for source2 operand ++ */ ++ emulate_inst = (emulate_inst & 0xffff07ff) | 0x800; ++ } ++ ++ if (likely(op != MOVEI)) { ++ /* ++ * change emulated instruction source1 to d0 ++ */ ++ emulate_inst &= ~0x7ff; ++ emulate_inst |= 1 << 8; ++ } ++ ++ if (unlikely(op == 6 || op == 2)) { ++ /* ++ * Set destination to d0 ++ */ ++ emulate_inst &= ~(0xf << 16); ++ } else if (likely(op != CMPI)) { ++ /* ++ * Set general destination field to d0. ++ */ ++ emulate_inst &= ~(0x7ff << 16); ++ emulate_inst |= 1 << 24; ++ } ++ ++ /* ++ * execute emulated instruction d0, to d0, no memory access ++ * source2 if needed will be in d1 ++ * source3, CSR, and accumulators are set up before execution ++ */ ++ *((unsigned int *)&trap_emulate) = emulate_inst; ++ flush_dcache_range((unsigned long)(&trap_emulate), ++ (unsigned long)(&trap_emulate) + 4); ++ ++ result = ubicom32_emulate_insn(source1, source2, source3, ++ save_acc, &save_csr); ++ ++ /* ++ * set the result value ++ */ ++ if (dest_operand) { ++ /* ++ * copy result to memory ++ */ ++ if (four_byte) { ++ *dest_address++ = ++ (unsigned char)((result >> 24) & 0xff); ++ *dest_address++ = ++ (unsigned char)((result >> 16) & 0xff); ++ } ++ *dest_address++ = (unsigned char)((result >> 8) & 0xff); ++ *dest_address = (unsigned char)(result & 0xff); ++ } else if (likely(op != CMPI)) { ++ /* ++ * copy result to a register, using move.4 dest, result ++ */ ++ extern unsigned int unaligned_trap_set_result; ++ *((unsigned int *)&unaligned_trap_set_result) &= ~0x7ff0000; ++ ++ if (op == 2 || op == 6) { ++ *((unsigned int *)&unaligned_trap_set_result) |= ++ ((inst & 0x000f0000) | 0x01000000); ++ } else { ++ *((unsigned int *)&unaligned_trap_set_result) |= ++ (inst & 0x7ff0000); ++ } ++ flush_dcache_range((unsigned long)&unaligned_trap_set_result, ++ ((unsigned long)(&unaligned_trap_set_result) + 4)); ++ ++ asm volatile ( ++ /* result uses thread's registers */ ++ " move.4 CSR, %1 \n\t" ++ " setcsr_flush 0 \n\t" ++ "unaligned_trap_set_result: \n\t" ++ " move.4 #0, %0 \n\t" ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ : ++ : "d"(result), "d" ((1 << 14) | (thread << 15)) ++ : "cc" ++ ); ++ } ++ ++ /* ++ * bump PC in thread and restore implicit register changes ++ */ ++ asm volatile ( ++ " move.4 CSR, %0 \n\t" ++ " setcsr_flush 0 \n\t" ++ " move.4 pc, %1 \n\t" ++ " move.4 acc0_lo, (%3) \n\t" ++ " move.4 acc0_hi, 4(%3) \n\t" ++ " move.4 acc1_lo, 8(%3) \n\t" ++ " move.4 acc1_hi, 12(%3) \n\t" ++ " move.4 mac_rc16, 16(%3) \n\t" ++ " move.4 CSR, %2 \n\t" ++ " setcsr #0 \n\t" ++ " setcsr_flush 0 \n\t" ++ : ++ : "d"((1 << 14) | (thread << 15)), ++ "d"(pc + 4), "d"(save_csr), "a"(save_acc) ++ : "cc" ++ ); ++} ++ ++/* ++ * unaligned_only() ++ * Return true if either of the unaligned causes are set (and no others). ++ */ ++int unaligned_only(unsigned int cause) ++{ ++ unsigned int unaligned_cause_mask = ++ (1 << TRAP_CAUSE_DST_MISALIGNED) | ++ (1 << TRAP_CAUSE_SRC1_MISALIGNED); ++ ++ BUG_ON(cause == 0); ++ return (cause & unaligned_cause_mask) == cause; ++} +--- /dev/null ++++ b/arch/ubicom32/kernel/vmlinux.lds.S +@@ -0,0 +1,303 @@ ++/* ++ * arch/ubicom32/kernel/vmlinux.lds.S ++ * vmlinux primary linker script ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <asm-generic/vmlinux.lds.h> ++#include <asm/ocm_size.h> ++#include <asm/thread_info.h> ++#include <linux/threads.h> ++ ++/* ++ * Sanity checks to prevent errors later on that are much harder to understand ++ */ ++#if !defined APP_OCM_CODE_SIZE ++#error APP_OCM_CODE_SIZE has not been defined in ocm_size.h ++#endif ++ ++#if !defined APP_OCM_DATA_SIZE ++#error APP_OCM_DATA_SIZE has not been defined in ocm_size.h ++#endif ++ ++/* ++ * XXX Should get these from a common header file, but <asm/ip5000.h> is a mess ++ * and can't be included by anything other than C code at the moment. ++ */ ++/* ++ * Memory Size ++ */ ++#define OCM_SECTOR_SIZE 0x00008000 /* 32K */ ++ ++#if defined(CONFIG_UBICOM32_V3) ++#define OCMSIZE 0x00030000 /* 192K on-chip RAM for both program and data */ ++#elif defined(CONFIG_UBICOM32_V4) ++#define OCMSIZE 0x0003C000 /* 240K on-chip RAM for both program and data */ ++#else ++#error "Unknown IP5K silicon" ++#endif ++ ++#define OCMSTART 0x3ffc0000 /* alias from 0x03000000 for easy jump to/from SDRAM */ ++#define OCMEND (OCMSTART + OCMSIZE) ++ ++/* ++ * The `free' ocm area that ultra does not use. ++ */ ++#if APP_OCM_CODE_SIZE || APP_OCM_DATA_SIZE ++#define OCM_FREE_START (OCMSTART + APP_OCM_CODE_SIZE) ++#define OCM_FREE_LENGTH (OCMSIZE - APP_OCM_CODE_SIZE - APP_OCM_DATA_SIZE) ++#else ++#define OCM_FREE_START OCMEND ++#define OCM_FREE_LENGTH 0 ++#endif ++/* ++ * If you want to limit OCM use for text/data or completely disable it you can change these values. ++ */ ++#define OCM_TEXT_LENGTH OCM_FREE_LENGTH ++#define OCM_DATA_LENGTH OCM_FREE_LENGTH ++ ++#define RAM_START CONFIG_KERNELBASE ++#define RAM_LENGTH (CONFIG_RAMBASE + CONFIG_RAMSIZE - CONFIG_KERNELBASE) ++#define TEXT ram ++#define DATA ram ++#define INIT ram ++#define BSS ram ++ ++#ifndef DATA_ADDR ++#define DATA_ADDR ++#endif ++ ++OUTPUT_ARCH(ubicom32) ++ENTRY(_start) ++ ++MEMORY { ++ ram : ORIGIN = RAM_START, LENGTH = RAM_LENGTH ++ ocm : ORIGIN = OCM_FREE_START, LENGTH = OCM_FREE_LENGTH ++} ++ ++jiffies = jiffies_64 + 4; ++ ++/* ++ * Fixed locations required by gdb coredumps. ++ * ++ * Note that the names are what gdb is expecting so renaming will break ++ * the toolchain. ++ */ ++__ocm_begin = OCMSTART; ++__ocm_limit = __ocm_begin + OCMSIZE; ++__sdram_begin = CONFIG_RAMBASE; ++__sdram_limit = __sdram_begin + CONFIG_RAMSIZE; ++__filemedia_begin_addr = 0x60000000; ++__filemedia_end_addr = __filemedia_begin_addr + 0x00800000; ++ ++SECTIONS { ++ ++ .fixed_text : { ++ _begin = .; ++ *(.skip_syscall) ++ *(.syscall_entry.text) ++ *(.syscall_exit.text) ++ *(.ret_from_interrupt_to_user.text) ++ *(.ret_from_fork.text) ++ *(.kernel_unprotected) ++ __fixed_text_end = .; ++ } > TEXT ++ . = _begin + SIZEOF(.fixed_text) ; ++ ++ __ocm_text_load_begin = .; ++ __ocm_text_run_begin = OCM_FREE_START ; ++ .ocm_text __ocm_text_run_begin : AT(__ocm_text_load_begin) { ++#if OCM_TEXT_LENGTH ++ *(.ocm_text) ++ *(.sched.text) ++ *(.spinlock.text) ++#include <asm/ocm_text.lds.inc> ++ . = ALIGN(4); ++#endif ++ __ocm_text_run_end = .; ++ __data_begin = ALIGN(OCM_SECTOR_SIZE); ++ } > ocm /* .ocm_text */ ++ ++ .ocm_module_text __ocm_text_run_end (NOLOAD) : AT(__ocm_text_run_end) { ++ __ocm_inst_heap_begin = .; ++ /* Reserve the min requested */ ++ . += (CONFIG_OCM_MODULES_RESERVATION) * 1024; ++#ifdef CONFIG_OCM_MODULES_MAY_CONSUME_REMAINING_CODESPACE ++ /* Round up to OCM sector size (we cannot use it for data) */ ++ . = ALIGN(OCM_SECTOR_SIZE); ++#endif ++ __ocm_inst_heap_end = .; ++ /* update __data_begin */ ++ __data_begin = ALIGN(OCM_SECTOR_SIZE); ++ } > ocm /* .ocm_module_text */ ++ ++ . = __ocm_text_load_begin + __ocm_text_run_end - __ocm_text_run_begin ; ++ __ocm_text_load_end = .; ++ ++ __ocm_data_load_begin = .; ++ __ocm_data_run_begin = __data_begin ; ++#if OCM_DATA_LENGTH ++ .ocm_data __ocm_data_run_begin : AT(__ocm_data_load_begin) { ++#if defined(CONFIG_IRQSTACKS_USEOCM) ++ percpu_irq_stacks = .; ++ . += NR_CPUS * THREAD_SIZE; ++#endif ++ *(.ocm_data) ++ . = ALIGN(4) ; ++ __ocm_data_run_end = .; ++ } > ocm ++ . = __ocm_data_load_begin + __ocm_data_run_end - __ocm_data_run_begin ; ++#else ++ __ocm_data_run_end = __ocm_data_run_begin; ++#endif ++ __ocm_data_load_end = .; ++ ++ __ocm_free_begin = __ocm_data_run_end; ++ __ocm_free_end = OCM_FREE_START + OCM_FREE_LENGTH; ++ ++ .text __ocm_data_load_end : AT(__ocm_data_load_end) { ++ . = ALIGN(4); ++ _stext = .; ++ _text = .; ++ TEXT_TEXT ++ SCHED_TEXT ++ LOCK_TEXT ++ *(.text.lock) ++ *(.text.__libgcc_udivmodsi) ++ *(.text.__libgcc_divmodsi) ++ *(.text.__libgcc_muldi3) ++ *(.text.__libgcc_udivmoddi) ++ *(.text.__libgcc_divmoddi) ++ *(.text.*) ++#if OCM_TEXT_LENGTH == 0 ++ *(.ocm_text) ++ *(.sched.text) ++ *(.spinlock.text) ++#endif ++ . = ALIGN(16); /* Exception table */ ++ __start___ex_table = .; ++ *(__ex_table) ++ __stop___ex_table = .; ++ ++ } > TEXT ++ ++ RO_DATA(16) ++ ++ .rodata : {} > TEXT ++ .rodata1 : {} > TEXT ++ .pci_fixup : {} > TEXT ++ .builtin_fw : {} > TEXT ++ .rio_route : {} > TEXT ++ .tracedata : {} > TEXT ++ __ksymtab : {} > TEXT ++ __ksymtab_gpl : {} > TEXT ++ __ksymtab_gpl_future : {} > TEXT ++ __kcrctab_gpl : {} > TEXT ++ __kcrctab_unused : {} > TEXT ++ __kcrctab_unused_gpl : {} > TEXT ++ __kcrctab_gpl_future : {} > TEXT ++ __ksymtab_strings : {} > TEXT ++ __init_rodata : {} > TEXT ++ __param : {} > TEXT ++ ++ _etext = .; ++ ++ .data DATA_ADDR : { ++ . = ALIGN(4); ++ _sdata = . ; ++ DATA_DATA ++#if OCM_DATA_LENGTH == 0 ++ *(.ocm_data) ++#endif ++ . = ALIGN(8192) ; ++ _data_protection_end = .; ++ *(.data.init_task) ++ . = ALIGN(4); ++ _edata = . ; ++ } > DATA ++ ++ .init : { ++ . = ALIGN(4096); ++ __init_begin = .; ++ _sinittext = .; ++ INIT_TEXT ++ _einittext = .; ++ *(.init.rodata) ++ INIT_DATA ++ . = ALIGN(16); ++ __setup_start = .; ++ *(.init.setup) ++ __setup_end = .; ++ __initcall_start = .; ++ INITCALLS ++ __initcall_end = .; ++ __con_initcall_start = .; ++ *(.con_initcall.init) ++ __con_initcall_end = .; ++ ___security_initcall_start = .; ++ *(.security_initcall.init) ++ ___security_initcall_end = .; ++#ifdef CONFIG_BLK_DEV_INITRD ++ . = ALIGN(4); ++ __initramfs_start = .; ++ *(.init.ramfs) ++ __initramfs_end = .; ++#endif ++ . = ALIGN(4096); ++ __per_cpu_start = .; ++ *(.data.percpu) ++ *(.data.percpu.shared_aligned) ++ __per_cpu_end = .; ++ ++ . = ALIGN(4096); ++ __init_end = .; ++ } > INIT ++ ++ .eh_frame : ++ { ++ PROVIDE (___eh_frame_begin = .); ++ *(.eh_frame) ++ LONG (0); ++ PROVIDE (___eh_frame_end = .); ++ } > INIT ++ ++ .bss : { ++ . = ALIGN(4); ++ _sbss = . ; ++ *(.bss) ++ *(COMMON) ++ . = ALIGN(4) ; ++ _ebss = . ; ++ _end = . ; ++ } > BSS ++ ++ /DISCARD/ : { ++ EXIT_TEXT ++ EXIT_DATA ++ *(.exitcall.exit) ++ } ++ ++ NOTES > BSS ++ ++} +--- /dev/null ++++ b/arch/ubicom32/lib/checksum.c +@@ -0,0 +1,250 @@ ++/* ++ * arch/ubicom32/lib/checksum.c ++ * Optimized checksum utilities for IP. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++/* ++ * INET An implementation of the TCP/IP protocol suite for the LINUX ++ * operating system. INET is implemented using the BSD Socket ++ * interface as the means of communication with the user level. ++ * ++ * IP/TCP/UDP checksumming routines ++ * ++ * Authors: Jorge Cwik, <jorge@laser.satlink.net> ++ * Arnt Gulbrandsen, <agulbra@nvg.unit.no> ++ * Tom May, <ftom@netcom.com> ++ * Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de> ++ * Lots of code moved from tcp.c and ip.c; see those files ++ * for more names. ++ * ++ * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek: ++ * Fixed some nasty bugs, causing some horrible crashes. ++ * A: At some points, the sum (%0) was used as ++ * length-counter instead of the length counter ++ * (%1). Thanks to Roman Hodek for pointing this out. ++ * B: GCC seems to mess up if one uses too many ++ * data-registers to hold input values and one tries to ++ * specify d0 and d1 as scratch registers. Letting gcc choose these ++ * registers itself solves the problem. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access kills, so most ++ of the assembly has to go. */ ++ ++#include <linux/module.h> ++#include <net/checksum.h> ++ ++static unsigned long do_csum(const unsigned char * buff, int len) ++{ ++ int count; ++ unsigned long result = 0; ++ ++ /* ++ * The following optimized assembly code cannot handle data length less than 7 bytes! ++ */ ++ if (likely(len >= 7)) { ++ len -= (4 - (int)buff) & 3; ++ count = len >> 2; ++ asm ( ++ " sub.4 d15, #0, %2 \n\t" // set up for jump table ++ " and.4 d15, #(32-1), d15 \n\t" // d15 = (-m) & (32 - 1) ++ ++ " bfextu d14, %0, #2 \n\t" // test 2 LSB of buff ++ " jmpne.w.f 100f \n\t" ++ " add.4 %1, #0, %1 \n\t" // clear C ++ " moveai a3, #%%hi(1f) \n\t" // table jump ++ " lea.1 a3, %%lo(1f)(a3) \n\t" ++ " lea.4 a3, (a3,d15) \n\t" ++ " calli a3, 0(a3) \n\t" ++ ++ "100: sub.4 %0, %0, d14 \n\t" ++ " sub.4 d14, #4, d14 \n\t" ++ " lsl.4 d14, d14, #3 \n\t" ++ " add.4 %1, #0, %1 \n\t" // clear C ++ " moveai a3, #%%hi(1f) \n\t" // table jump ++ " lea.1 a3, %%lo(1f)(a3) \n\t" ++ " lea.4 a3, (a3,d15) \n\t" ++ " bfextu %1, (%0)4++, d14 \n\t" // read first partial word ++ " calli a3, 0(a3) \n\t" ++#if 1 ++ "200: lsl.4 %3, %3, #3 \n\t" ++ " bfrvrs d15, (%0), #0 \n\t" // read last word (partial) ++ " bfextu d15, d15, %3 \n\t" ++ " bfrvrs d15, d15, #0 \n\t" ++ " add.4 %1, d15, %1 \n\t" ++ " addc %1, #0, %1 \n\t" // sample C again ++ " jmpt.w.t 2f \n\t" ++#else ++ "200: move.1 d15, 0(%0) \n\t" ++ " lsl.4 d15, d15, #8 \n\t" ++ " add.4 %1, d15, %1 \n\t" ++ " addc %1, #0, %1 \n\t" // sample C again ++ " add.4 %3, #-1, %3 \n\t" ++ " jmpeq.w.t 2f \n\t" ++ ++ " move.1 d15, 1(%0) \n\t" ++ " add.4 %1, d15, %1 \n\t" ++ " addc %1, #0, %1 \n\t" // sample C again ++ " add.4 %3, #-1, %3 \n\t" ++ " jmpeq.w.t 2f \n\t" ++ ++ " move.1 d15, 2(%0) \n\t" ++ " lsl.4 d15, d15, #8 \n\t" ++ " add.4 %1, d15, %1 \n\t" ++ " addc %1, #0, %1 \n\t" // sample C again ++ " jmpt.w.t 2f \n\t" ++#endif ++#if defined(IP7000) || defined(IP7000_REV2) ++ "300: swapb.2 %1, %1 \n\t" ++#else ++ "300: shmrg.2 %1, %1, %1 \n\t" ++ " lsr.4 %1, %1, #8 \n\t" ++ " bfextu %1, %1, #16 \n\t" ++#endif ++ " jmpt.w.t 3f \n\t" ++ ++ "1: add.4 %1, (%0)4++, %1 \n\t" // first add without C ++ " .rept 31 \n\t" ++ " addc %1, (%0)4++, %1 \n\t" ++ " .endr \n\t" ++ " addc %1, #0, %1 \n\t" // sample C again ++ " add.4 %2, #-32, %2 \n\t" ++ " jmpgt.w.t 1b \n\t" ++ ++ " and.4 %3, #3, %3 \n\t" // check n ++ " jmpne.w.f 200b \n\t" ++ ++ "2: .rept 2 \n\t" ++ " lsr.4 d15, %1, #16 \n\t" ++ " bfextu %1, %1, #16 \n\t" ++ " add.4 %1, d15, %1 \n\t" ++ " .endr \n\t" ++ " btst d14, #3 \n\t" // start from odd address (<< 3)? ++ " jmpne.w.f 300b \n\t" ++ "3: \n\t" ++ ++ : "+a"(buff), "+d"(result), "+d"(count), "+d"(len) ++ : ++ : "d15", "d14", "a3", "cc" ++ ); ++ ++ return result; ++ } ++ ++ /* ++ * handle a few bytes and fold result into 16-bit ++ */ ++ while (len-- > 0) { ++ result += (*buff++ << 8); ++ if (len) { ++ result += *buff++; ++ len--; ++ } ++ } ++ asm ( ++ " .rept 2 \n\t" ++ " lsr.4 d15, %0, #16 \n\t" ++ " bfextu %0, %0, #16 \n\t" ++ " add.4 %0, d15, %0 \n\t" ++ " .endr \n\t" ++ : "+d" (result) ++ : ++ : "d15", "cc" ++ ); ++ ++ return result; ++} ++ ++/* ++ * This is a version of ip_compute_csum() optimized for IP headers, ++ * which always checksum on 4 octet boundaries. ++ */ ++__sum16 ip_fast_csum(const void *iph, unsigned int ihl) ++{ ++ return (__force __sum16)~do_csum(iph,ihl*4); ++} ++ ++/* ++ * computes the checksum of a memory block at buff, length len, ++ * and adds in "sum" (32-bit) ++ * ++ * returns a 32-bit number suitable for feeding into itself ++ * or csum_tcpudp_magic ++ * ++ * this function must be called with even lengths, except ++ * for the last fragment, which may be odd ++ * ++ * it's best to have buff aligned on a 32-bit boundary ++ */ ++__wsum csum_partial(const void *buff, int len, __wsum sum) ++{ ++ unsigned int result = do_csum(buff, len); ++ ++ /* add in old sum, and carry.. */ ++ result += (__force u32)sum; ++ if ((__force u32)sum > result) ++ result += 1; ++ return (__force __wsum)result; ++} ++ ++EXPORT_SYMBOL(csum_partial); ++ ++/* ++ * this routine is used for miscellaneous IP-like checksums, mainly ++ * in icmp.c ++ */ ++__sum16 ip_compute_csum(const void *buff, int len) ++{ ++ return (__force __sum16)~do_csum(buff,len); ++} ++ ++/* ++ * copy from fs while checksumming, otherwise like csum_partial ++ */ ++ ++__wsum ++csum_partial_copy_from_user(const void __user *src, void *dst, ++ int len, __wsum sum, int *csum_err) ++{ ++ if (csum_err) *csum_err = 0; ++ memcpy(dst, (__force const void *)src, len); ++ return csum_partial(dst, len, sum); ++} ++ ++/* ++ * copy from ds while checksumming, otherwise like csum_partial ++ */ ++ ++__wsum ++csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum) ++{ ++ memcpy(dst, src, len); ++ return csum_partial(dst, len, sum); ++} +--- /dev/null ++++ b/arch/ubicom32/lib/delay.c +@@ -0,0 +1,49 @@ ++/* ++ * arch/ubicom32/lib/delay.c ++ * Ubicom32 implementation of udelay() ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <asm/param.h> ++#include <asm/delay.h> ++#include <asm/ip5000.h> ++ ++/* ++ * read_current_timer() ++ * Return the current value of sysval. ++ */ ++int __devinit read_current_timer(unsigned long *timer_val) ++{ ++ *timer_val = (long)(UBICOM32_IO_TIMER->sysval); ++ return 0; ++} ++ ++ ++void udelay(unsigned long usecs) ++{ ++ _udelay(usecs); ++} ++EXPORT_SYMBOL(udelay); +--- /dev/null ++++ b/arch/ubicom32/lib/Makefile +@@ -0,0 +1,32 @@ ++# ++# arch/ubicom32/lib/Makefile ++# <TODO: Replace with short file description> ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++# ++# Makefile for m68knommu specific library files.. ++# ++ ++lib-y := checksum.o delay.o mem_ubicom32.o muldi3.o lshrdi3.o ashldi3.o ashrdi3.o divmod.o +--- /dev/null ++++ b/arch/ubicom32/lib/mem_ubicom32.c +@@ -0,0 +1,242 @@ ++/* ++ * arch/ubicom32/lib/mem_ubicom32.c ++ * String functions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/compiler.h> ++ ++#define LIKELY likely ++#define UNLIKELY unlikely ++ ++typedef u32_t addr_t; ++ ++/* ++ * memcpy() ++ */ ++void *memcpy(void *dest, const void *src, size_t n) ++{ ++ void *dest_ret = dest; ++ ++ if (LIKELY((((addr_t)dest ^ (addr_t)src) & 3) == 0) && LIKELY(n > 6)) { ++ size_t m; ++ n -= (4 - (addr_t)dest) & 0x03; ++ m = n >> 2; ++ asm volatile ( ++ " sub.4 d15, #0, %2 \n\t" // set up for jump table ++ " and.4 d15, #(32-1), d15 \n\t" // d15 = (-m) & (32 - 1) ++ " moveai a3, #%%hi(1f) \n\t" ++ " lea.1 a3, %%lo(1f)(a3) \n\t" ++ " lea.4 a3, (a3,d15) \n\t" ++ ++ " bfextu d15, %0, #2 \n\t" // d15 = (dest & 3) ++ " jmpne.w.f 100f \n\t" ++ " calli a3, 0(a3) \n\t" // 4-byte alignment ++ ++ "100: cmpi d15, #2 \n\t" ++ " jmpne.s.f 101f \n\t" ++ " move.2 (%0)2++, (%1)2++ \n\t" ++ " calli a3, 0(a3) \n\t" // 2-byte alignment ++ ++ "101: move.1 (%0)1++, (%1)1++ \n\t" ++ " jmpgt.s.f 102f \n\t" // 3-byte alignment ++ " move.2 (%0)2++, (%1)2++ \n\t" // 1-byte alignment ++ "102: calli a3, 0(a3) \n\t" ++ ++ "200: cmpi %3, #2 \n\t" ++ " jmplt.s.f 201f \n\t" ++ " move.2 (%0)2++, (%1)2++ \n\t" ++ " jmpeq.s.t 2f \n\t" ++ "201: move.1 (%0)1++, (%1)1++ \n\t" ++ " jmpt.w.t 2f \n\t" ++ ++ "1: .rept 25 \n\t" ++ " movea (%0)4++, (%1)4++ \n\t" ++ " .endr \n\t" ++ " .rept 7 \n\t" ++ " move.4 (%0)4++, (%1)4++ \n\t" ++ " .endr \n\t" ++ " add.4 %2, #-32, %2 \n\t" ++ " jmpgt.w.f 1b \n\t" ++ ++ " and.4 %3, #3, %3 \n\t" // check n ++ " jmpne.w.f 200b \n\t" ++ "2: \n\t" ++ : "+a" (dest), "+a" (src), "+d" (m), "+d" (n) ++ : ++ : "d15", "a3", "memory", "cc" ++ ); ++ ++ return dest_ret; ++ } ++ ++ if (LIKELY((((addr_t)dest ^ (addr_t)src) & 1) == 0) && LIKELY(n > 2)) { ++ size_t m; ++ n -= (addr_t)dest & 0x01; ++ m = n >> 1; ++ asm volatile ( ++ " sub.4 d15, #0, %2 \n\t" // set up for jump table ++ " and.4 d15, #(32-1), d15 \n\t" // d15 = (-m) & (32 - 1) ++ " moveai a3, #%%hi(1f) \n\t" ++ " lea.1 a3, %%lo(1f)(a3) \n\t" ++ " lea.4 a3, (a3,d15) \n\t" ++ ++ " btst %0, #0 \n\t" // check bit 0 ++ " jmpne.w.f 100f \n\t" ++ " calli a3, 0(a3) \n\t" // 4-byte alignment ++ ++ "100: move.1 (%0)1++, (%1)1++ \n\t" ++ " calli a3, 0(a3) \n\t" ++ ++ "200: move.1 (%0)1++, (%1)1++ \n\t" ++ " jmpt.w.t 2f \n\t" ++ ++ "1: .rept 32 \n\t" ++ " move.2 (%0)2++, (%1)2++ \n\t" ++ " .endr \n\t" ++ " add.4 %2, #-32, %2 \n\t" ++ " jmpgt.w.f 1b \n\t" ++ ++ " and.4 %3, #1, %3 \n\t" // check n ++ " jmpne.w.f 200b \n\t" ++ "2: \n\t" ++ ++ : "+a" (dest), "+a" (src), "+d" (m), "+d" (n) ++ : ++ : "d15", "a3", "memory", "cc" ++ ); ++ ++ return dest_ret; ++ } ++ ++ asm volatile ( ++ " sub.4 d15, #0, %2 \n\t" ++ " jmpeq.w.f 2f \n\t" ++ " and.4 d15, #(16-1), d15 \n\t" // d15 = (-n) & (16 - 1) ++ " moveai a3, #%%hi(1f) \n\t" ++ " lea.1 a3, %%lo(1f)(a3) \n\t" ++ " lea.4 a3, (a3,d15) \n\t" ++ " calli a3, 0(a3) \n\t" ++ ++ "1: .rept 16 \n\t" ++ " move.1 (%0)1++, (%1)1++ \n\t" ++ " .endr \n\t" ++ " add.4 %2, #-16, %2 \n\t" ++ " jmpgt.w.f 1b \n\t" ++ "2: \n\t" ++ ++ : "+a" (dest), "+a" (src), "+d" (n) ++ : ++ : "d15", "a3", "memory", "cc" ++ ); ++ ++ return dest_ret; ++} ++ ++/* ++ * memset() ++ */ ++void *memset(void *s, int c, size_t n) ++{ ++ void *s_ret = s; ++ ++ if (LIKELY(n > 6)) { ++ size_t m; ++ n -= (4 - (addr_t)s) & 0x03; ++ m = n >> 2; ++ asm volatile ( ++ " sub.4 d15, #0, %2 \n\t" // set up for jump table ++ " and.4 d15, #(32-1), d15 \n\t" // d15 = (-m) & (32 - 1) ++ " shmrg.1 %1, %1, %1 \n\t" ++ " shmrg.2 %1, %1, %1 \n\t" // %1 = (c<<24)|(c<<16)|(c<<8)|c ++ " moveai a3, #%%hi(1f) \n\t" ++ " lea.1 a3, %%lo(1f)(a3) \n\t" ++ " lea.4 a3, (a3,d15) \n\t" ++ ++ " bfextu d15, %0, #2 \n\t" // d15 = (s & 3) ++ " jmpne.w.f 100f \n\t" ++ " calli a3, 0(a3) \n\t" // 4-byte alignment ++ ++ "100: cmpi d15, #2 \n\t" ++ " jmpne.s.f 101f \n\t" ++ " move.2 (%0)2++, %1 \n\t" ++ " calli a3, 0(a3) \n\t" // 2-byte alignment ++ ++ "101: move.1 (%0)1++, %1 \n\t" ++ " jmpgt.s.f 102f \n\t" // 3-byte alignment ++ " move.2 (%0)2++, %1 \n\t" // 1-byte alignment ++ "102: calli a3, 0(a3) \n\t" ++ ++ "200: cmpi %3, #2 \n\t" ++ " jmplt.s.f 201f \n\t" ++ " move.2 (%0)2++, %1 \n\t" ++ " jmpeq.s.t 2f \n\t" ++ "201: move.1 (%0)1++, %1 \n\t" ++ " jmpt.w.t 2f \n\t" ++ ++ "1: .rept 25 \n\t" ++ " movea (%0)4++, %1 \n\t" ++ " .endr \n\t" ++ " .rept 7 \n\t" ++ " move.4 (%0)4++, %1 \n\t" ++ " .endr \n\t" ++ " add.4 %2, #-32, %2 \n\t" ++ " jmpgt.w.f 1b \n\t" ++ ++ " and.4 %3, #3, %3 \n\t" // test bit 1 of n ++ " jmpne.w.f 200b \n\t" ++ "2: \n\t" ++ ++ : "+a" (s), "+d" (c), "+d" (m), "+d" (n) ++ : ++ : "d15", "a3", "memory", "cc" ++ ); ++ ++ return s_ret; ++ } ++ ++ asm volatile ( ++ " sub.4 d15, #0, %2 \n\t" ++ " jmpeq.w.f 2f \n\t" ++ " and.4 d15, #(8-1), d15 \n\t" // d15 = (-%2) & (16 - 1) ++ " moveai a3, #%%hi(1f) \n\t" ++ " lea.1 a3, %%lo(1f)(a3) \n\t" ++ " lea.4 a3, (a3,d15) \n\t" ++ " calli a3, 0(a3) \n\t" ++ ++ "1: .rept 8 \n\t" ++ " move.1 (%0)1++, %1 \n\t" ++ " .endr \n\t" ++ "2: \n\t" ++ ++ : "+a" (s), "+d" (c), "+d" (n) ++ : ++ : "d15", "a3", "memory", "cc" ++ ); ++ ++ return s_ret; ++} +--- /dev/null ++++ b/arch/ubicom32/mach-common/audio_tio.c +@@ -0,0 +1,115 @@ ++/* ++ * arch/ubicom32/mach-common/audio_tio.c ++ * Generic initialization for Ubicom32 Audio ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/types.h> ++ ++#include <asm/devtree.h> ++#include <asm/audio_tio.h> ++#include <asm/ubi32-pcm.h> ++ ++/* ++ * The number of audio_tio's currently allocated, used for .id ++ */ ++static int __initdata audio_tio_count; ++ ++/* ++ * The maximum number of resources that the audio_tio will have. ++ * Currently 3, a register space, and up to 2 interrupts. ++ */ ++#define AUDIO_TIO_MAX_RESOURCES 3 ++ ++/* ++ * audio_tio_alloc ++ * Checks the device tree and allocates a platform_device if found ++ */ ++struct platform_device * __init audio_tio_alloc(const char *driver_name, ++ const char *node_name, int priv_bytes) ++{ ++ struct platform_device *pdev; ++ struct resource *res; ++ struct audiotionode *audio_node; ++ struct ubi32pcm_platform_data *pdata; ++ ++ /* ++ * Check the device tree for the audio_tio ++ */ ++ audio_node = (struct audiotionode *)devtree_find_node(node_name); ++ if (!audio_node) { ++ printk(KERN_WARNING "audio TIO '%s' found\n", node_name); ++ return NULL; ++ } ++ ++ if (audio_node->version != AUDIOTIONODE_VERSION) { ++ printk(KERN_WARNING "audio_tio not compatible\n"); ++ return NULL; ++ } ++ ++ /* ++ * Dynamically create the platform_device structure and resources ++ */ ++ pdev = kzalloc(sizeof(struct platform_device) + ++ sizeof(struct ubi32pcm_platform_data) + ++ priv_bytes , GFP_KERNEL); ++ if (!pdev) { ++ printk(KERN_WARNING "audio_tio could not alloc pdev\n"); ++ return NULL; ++ } ++ ++ res = kzalloc(sizeof(struct resource) * AUDIO_TIO_MAX_RESOURCES, ++ GFP_KERNEL); ++ if (!res) { ++ kfree(pdev); ++ printk(KERN_WARNING "audio_tio could not alloc res\n"); ++ return NULL; ++ } ++ ++ pdev->name = driver_name; ++ pdev->id = audio_tio_count++; ++ pdev->resource = res; ++ ++ /* ++ * Fill in the resources and platform data from devtree information ++ */ ++ res[0].start = (u32_t)(audio_node->regs); ++ res[0].end = (u32_t)(audio_node->regs); ++ res[0].flags = IORESOURCE_MEM; ++ res[1 + AUDIOTIO_TX_IRQ_RESOURCE].start = audio_node->dn.sendirq; ++ res[1 + AUDIOTIO_TX_IRQ_RESOURCE].flags = IORESOURCE_IRQ; ++ res[1 + AUDIOTIO_RX_IRQ_RESOURCE].start = audio_node->dn.recvirq; ++ res[1 + AUDIOTIO_RX_IRQ_RESOURCE].flags = IORESOURCE_IRQ; ++ pdev->num_resources = 3; ++ ++ printk(KERN_INFO "AudioTIO.%d '%s' found irq=%d/%d regs=%p pdev=%p/%p\n", ++ audio_tio_count - 1, node_name, audio_node->dn.sendirq, ++ audio_node->dn.recvirq, audio_node->regs, pdev, res); ++ pdata = (struct ubi32pcm_platform_data *)(pdev + 1); ++ pdev->dev.platform_data = pdata; ++ pdata->node_name = node_name; ++ if (priv_bytes) { ++ pdata->priv_data = pdata + 1; ++ } ++ ++ return pdev; ++} ++ +--- /dev/null ++++ b/arch/ubicom32/mach-common/board.c +@@ -0,0 +1,63 @@ ++/* ++ * arch/ubicom32/mach-common/board.c ++ * Board init and support code. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/cpu.h> ++#include <asm/devtree.h> ++ ++struct boardnode { ++ struct devtree_node dn; ++ const char *revision; ++}; ++ ++static const struct boardnode *bn; ++ ++/* ++ * board_get_revision() ++ * Returns revision string of the board. ++ */ ++const char *board_get_revision(void) ++{ ++ if (!bn) { ++ return "NULL"; ++ } ++ ++ return bn->revision; ++} ++ ++/* ++ * board_init ++ */ ++void __init board_init(void) ++{ ++ bn = (struct boardnode *)devtree_find_node("board"); ++ if (!bn) { ++ printk(KERN_WARNING "board node not found\n"); ++ return; ++ } ++} +--- /dev/null ++++ b/arch/ubicom32/mach-common/bootargs.c +@@ -0,0 +1,63 @@ ++/* ++ * arch/ubicom32/mach-common/bootargs.c ++ * Board init and support code. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/cpu.h> ++#include <asm/devtree.h> ++ ++struct bootargsnode { ++ struct devtree_node dn; ++ const char cmdline[512]; ++}; ++ ++static const struct bootargsnode *ban; ++ ++/* ++ * bootargs_get_cmdline() ++ * Returns kernel boot arguments set by the bootloader. ++ */ ++const char *bootargs_get_cmdline(void) ++{ ++ if (!ban) { ++ return ""; ++ } ++ ++ return ban->cmdline; ++} ++ ++/* ++ * bootargs_init ++ */ ++void __init bootargs_init(void) ++{ ++ ban = (struct bootargsnode *)devtree_find_node("bootargs"); ++ if (!ban) { ++ printk(KERN_WARNING "bootargs node not found\n"); ++ return; ++ } ++} +--- /dev/null ++++ b/arch/ubicom32/mach-common/cachectl.c +@@ -0,0 +1,125 @@ ++/* ++ * arch/ubicom32/mach-common/cachectl.c ++ * Architecture cache control support ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/types.h> ++#include <linux/module.h> ++#include <asm/cachectl.h> ++ ++/* ++ * The write queue flush procedure in mem_cache_control needs to make ++ * DCACHE_WRITE_QUEUE_LENGTH writes to DDR (not OCM). Here we reserve some ++ * memory for this operation. ++ * Allocate array of cache lines of least DCACHE_WRITE_QUEUE_LENGTH + 1 words in ++ * length rounded up to the nearest cache line. ++ */ ++#define CACHE_WRITE_QUEUE_FLUSH_AREA_SIZE \ ++ ALIGN(sizeof(int) * (DCACHE_WRITE_QUEUE_LENGTH + 1), CACHE_LINE_SIZE) ++ ++static char cache_write_queue_flush_area[CACHE_WRITE_QUEUE_FLUSH_AREA_SIZE] ++ __attribute__((aligned(CACHE_LINE_SIZE))); ++ ++/* ++ * ONE_CCR_ADDR_OP is a helper macro that executes a single CCR operation. ++ */ ++#define ONE_CCR_ADDR_OP(cc, op_addr, op) \ ++ do { \ ++ asm volatile ( \ ++ " btst "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_VALID)" \n\t" \ ++ " jmpne.f .-4 \n\t" \ ++ " move.4 "D(CCR_ADDR)"(%0), %1 \n\t" \ ++ " move.1 "D(CCR_CTRL+3)"(%0), %2 \n\t" \ ++ " bset "D(CCR_CTRL)"(%0), "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_VALID)" \n\t" \ ++ " cycles 2 \n\t" \ ++ " btst "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_DONE)" \n\t" \ ++ " jmpeq.f .-4 \n\t" \ ++ : \ ++ : "a"(cc), "r"(op_addr), "r"(op & 0xff) \ ++ : "cc" \ ++ ); \ ++ } while (0) ++ ++/* ++ * mem_cache_control() ++ * Special cache control operation ++ */ ++void mem_cache_control(unsigned long cc, unsigned long begin_addr, ++ unsigned long end_addr, unsigned long op) ++{ ++ unsigned long op_addr; ++ ++ if (cc == DCCR_BASE && op == CCR_CTRL_FLUSH_ADDR) { ++ /* ++ * We ensure all previous writes have left the data cache write ++ * queue by sending DCACHE_WRITE_QUEUE_LENGTH writes (to ++ * different words) down the queue. If this is not done it's ++ * possible that the data we are trying to flush hasn't even ++ * entered the data cache. ++ * The +1 ensure that the final 'flush' is actually a flush. ++ */ ++ int *flush_area = (int *)cache_write_queue_flush_area; ++ asm volatile( ++ " .rept "D(DCACHE_WRITE_QUEUE_LENGTH + 1)" \n\t" ++ " move.4 (%0)4++, d0 \n\t" ++ " .endr \n\t" ++ : "+a"(flush_area) ++ ); ++ } ++ ++ /* ++ * Calculate the cache lines we need to operate on that include ++ * begin_addr though end_addr. ++ */ ++ begin_addr = begin_addr & ~(CACHE_LINE_SIZE - 1); ++ end_addr = (end_addr + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1); ++ op_addr = begin_addr; ++ ++ do { ++ ONE_CCR_ADDR_OP(cc, op_addr, op); ++ op_addr += CACHE_LINE_SIZE; ++ } while (likely(op_addr < end_addr)); ++ ++ if (cc == DCCR_BASE && op == CCR_CTRL_FLUSH_ADDR) { ++ /* ++ * It turns out that when flushing the data cache the last flush ++ * isn't actually complete at this point. This is because there ++ * is another write buffer on the DDR side of the cache that is ++ * arbitrated with the I-Cache. ++ * ++ * The only foolproof method that ensures that the last data ++ * cache flush *actually* completed is to do another flush on a ++ * dirty cache line. This flush will block until the DDR write ++ * buffer is empty. ++ * ++ * Rather than creating a another dirty cache line, we use the ++ * flush_area above as we know that it is dirty from previous ++ * writes. ++ */ ++ ONE_CCR_ADDR_OP(cc, cache_write_queue_flush_area, op); ++ } ++} ++EXPORT_SYMBOL(mem_cache_control); +--- /dev/null ++++ b/arch/ubicom32/mach-common/common.c +@@ -0,0 +1,64 @@ ++/* ++ * arch/ubicom32/mach-common/common.c ++ * Common platform support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/list.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/string.h> ++#include <linux/clk.h> ++#include <linux/mutex.h> ++#include <linux/platform_device.h> ++ ++ ++/* Minimum CLK support */ ++ ++struct clk *clk_get(struct device *dev, const char *id) ++{ ++ return ERR_PTR(-ENOENT); ++} ++EXPORT_SYMBOL(clk_get); ++ ++void clk_put(struct clk *clk) ++{ ++} ++EXPORT_SYMBOL(clk_put); ++ ++int clk_enable(struct clk *clk) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(clk_enable); ++ ++ ++void clk_disable(struct clk *clk) ++{ ++} ++EXPORT_SYMBOL(clk_disable); +--- /dev/null ++++ b/arch/ubicom32/mach-common/io.c +@@ -0,0 +1,250 @@ ++/* ++ * arch/ubicom32/mach-common/io.c ++ * PCI I/O memory read/write support functions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/io.h> ++ ++#ifdef CONFIG_PCI ++unsigned char ioread8(void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ return ubi32_pci_read_u8(addr); ++ else ++ return (unsigned char)(*(volatile unsigned char *)addr); ++} ++EXPORT_SYMBOL(ioread8); ++ ++unsigned short ioread16(void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ return ubi32_pci_read_u16(addr); ++ else ++ return (unsigned short)(*(volatile unsigned short *)addr); ++} ++EXPORT_SYMBOL(ioread16); ++ ++unsigned int ioread32(void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ return ubi32_pci_read_u32(addr); ++ else ++ return (unsigned int)(*(volatile unsigned int *)addr); ++} ++EXPORT_SYMBOL(ioread32); ++ ++void iowrite32(unsigned int val, void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ ubi32_pci_write_u32(val, addr); ++ else ++ *(volatile unsigned int *)addr = val; ++} ++EXPORT_SYMBOL(iowrite32); ++ ++void iowrite16(unsigned short val, void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ ubi32_pci_write_u16(val, addr); ++ else ++ *(volatile unsigned short *)addr = val; ++} ++EXPORT_SYMBOL(iowrite16); ++ ++void iowrite8(unsigned char val, void __iomem *addr) ++{ ++ if (IS_PCI_ADDRESS(addr)) ++ ubi32_pci_write_u8(val, addr); ++ else ++ *(volatile unsigned char *)addr = val; ++} ++EXPORT_SYMBOL(iowrite8); ++ ++void memcpy_fromio(void *to, const volatile void __iomem *from, unsigned len) ++{ ++ if (IS_PCI_ADDRESS(from)) { ++ if ((((u32_t)from & 0x3) == 0) && (((u32_t)to & 0x3) == 0)) { ++ while ((int)len >= 4) { ++ *(u32_t *)to = ubi32_pci_read_u32(from); ++ to += 4; ++ from += 4; ++ len -= 4; ++ } ++ } else if ((((u32_t)from & 0x1) == 0) && ++ (((u32_t)to & 0x1) == 0)) { ++ while ((int)len >= 2) { ++ *(u16_t *)to = ubi32_pci_read_u16(from); ++ to += 2; ++ from += 2; ++ len -= 2; ++ } ++ } ++ ++ while (len) { ++ *(u8_t *)to = ubi32_pci_read_u8(from); ++ to++; ++ from++; ++ len--; ++ } ++ } else ++ memcpy(to, (void *)from, len); ++} ++EXPORT_SYMBOL(memcpy_fromio); ++ ++void memcpy_toio(volatile void __iomem *to, const void *from, unsigned len) ++{ ++ if (IS_PCI_ADDRESS(to)) { ++ if ((((u32_t)from & 0x3) == 0) && (((u32_t)to & 0x3) == 0)) { ++ while ((int)len >= 4) { ++ ubi32_pci_write_u32(*(u32_t *)from, to); ++ to += 4; ++ from += 4; ++ len -= 4; ++ } ++ } else if ((((u32_t)from & 0x1) == 0) && ++ (((u32_t)to & 0x1) == 0)) { ++ while ((int)len >= 2) { ++ ubi32_pci_write_u16(*(u16_t *)from, to); ++ to += 2; ++ from += 2; ++ len -= 2; ++ } ++ } ++ ++ while (len) { ++ ubi32_pci_write_u8(*(u8_t *)from, to); ++ from++; ++ to++; ++ len--; ++ } ++ } else ++ memcpy((void *)to, from, len); ++ ++} ++EXPORT_SYMBOL(memcpy_toio); ++ ++void memset_io(volatile void __iomem *addr, int val, size_t len) ++{ ++ if (IS_PCI_ADDRESS(addr)) { ++ while (len) { ++ ubi32_pci_write_u8((unsigned char)val, addr); ++ addr++; ++ len--; ++ } ++ } else ++ memset((void *)addr, val, len); ++ ++} ++EXPORT_SYMBOL(memset_io); ++ ++void ioread8_rep(void __iomem *port, void *buf, unsigned long count) ++{ ++ if (IS_PCI_ADDRESS(port)) { ++ while (count) { ++ *(u8_t *)buf = ioread8(port); ++ buf++; ++ count--; ++ } ++ } else { ++ insb((unsigned int)port, buf, count); ++ } ++ ++} ++EXPORT_SYMBOL(ioread8_rep); ++ ++void ioread16_rep(void __iomem *port, void *buf, unsigned long count) ++{ ++ if (IS_PCI_ADDRESS(port)) { ++ while (count) { ++ *(u16_t *)buf = ioread16(port); ++ buf += 2; ++ count--; ++ } ++ } else { ++ insw((unsigned int)port, buf, count); ++ } ++} ++EXPORT_SYMBOL(ioread16_rep); ++ ++void ioread32_rep(void __iomem *port, void *buf, unsigned long count) ++{ ++ if (IS_PCI_ADDRESS(port)) { ++ while (count) { ++ *(u32_t *)buf = ioread32(port); ++ buf += 4; ++ count--; ++ } ++ } else { ++ insl((unsigned int)port, buf, count); ++ } ++} ++EXPORT_SYMBOL(ioread32_rep); ++ ++void iowrite8_rep(void __iomem *port, const void *buf, unsigned long count) ++{ ++ if (IS_PCI_ADDRESS(port)) { ++ while (count) { ++ iowrite8(*(u8_t *)buf, port); ++ buf++; ++ count--; ++ } ++ } else { ++ outsb((unsigned int)port, buf, count); ++ } ++ ++} ++EXPORT_SYMBOL(iowrite8_rep); ++ ++void iowrite16_rep(void __iomem *port, const void *buf, unsigned long count) ++{ ++ if (IS_PCI_ADDRESS(port)) { ++ while (count) { ++ iowrite16(*(u16_t *)buf, port); ++ buf += 2; ++ count--; ++ } ++ } else { ++ outsw((unsigned int)port, buf, count); ++ } ++} ++EXPORT_SYMBOL(iowrite16_rep); ++ ++void iowrite32_rep(void __iomem *port, const void *buf, unsigned long count) ++{ ++ if (IS_PCI_ADDRESS(port)) { ++ while (count) { ++ iowrite32(*(u32_t *)buf, port); ++ buf += 4; ++ count--; ++ } ++ } else { ++ outsl((unsigned int)port, buf, count); ++ } ++} ++EXPORT_SYMBOL(iowrite32_rep); ++ ++#endif /* CONFIG_PCI */ +--- /dev/null ++++ b/arch/ubicom32/mach-common/Makefile +@@ -0,0 +1,38 @@ ++# ++# arch/ubicom32/mach-common/Makefile ++# Makefile for Ubicom32 generic drivers/code. ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++ ++obj-y += cachectl.o common.o usb_tio.o usb.o ubi32-gpio.o board.o bootargs.o ++obj-$(CONFIG_PCI) += pci.o io.o ++ ++obj-$(CONFIG_FB_UBICOM32) += vdc_tio.o ++obj-$(CONFIG_UBICOM_HID) += ubicom32hid.o ++obj-$(CONFIG_UBICOM_INPUT) += ubicom32input.o ++obj-$(CONFIG_UBICOM_INPUT_I2C) += ubicom32input_i2c.o ++obj-$(CONFIG_UIO_UBICOM32RING) += ring_tio.o ++obj-$(CONFIG_SND_UBI32) += audio_tio.o ++ +--- /dev/null ++++ b/arch/ubicom32/mach-common/pci.c +@@ -0,0 +1,1157 @@ ++/* ++ * arch/ubicom32/mach-common/pci.c ++ * PCI interface management. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/pci.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/seq_file.h> ++#include <linux/proc_fs.h> ++ ++#include <asm/devtree.h> ++#include <asm/ip5000.h> ++#include <asm/ubicom32-common.h> ++ ++static int debug_pci = 1 ; ++ ++/* #define PCI_USE_INTERNAL_LOCK 1 */ ++ ++#ifdef PCI_USE_INTERNAL_LOCK ++#define PCI_LOCK(lock, irqflag) pci_lock_acquire(irqflag) ++#define PCI_UNLOCK(lock, irqflag) pci_lock_release(irqflag) ++#elif defined(CONFIG_SMP) ++static DEFINE_SPINLOCK(pci_master_lock); ++#define PCI_LOCK(lock, irqflag) spin_lock_irqsave(lock, irqflag) ++#define PCI_UNLOCK(lock, irqflag) spin_unlock_irqrestore(lock, irqflag) ++#else ++#define PCI_LOCK(lock, irqflag) local_irq_save(irqflag) ++#define PCI_UNLOCK(lock, irqflag) local_irq_restore(irqflag) ++#endif ++ ++#define PCI_DEV0_IDSEL CONFIG_PCI_DEV0_IDSEL ++#define PCI_DEV1_IDSEL CONFIG_PCI_DEV1_IDSEL ++ ++/* ++ * PCI commands ++ */ ++#define PCI_CMD_INT_ACK 0x00 /* not supported */ ++#define PCI_CMD_SPECIAL 0x01 /* not supported */ ++#define PCI_CMD_IO_READ 0x02 ++#define PCI_CMD_IO_WRITE 0x03 ++#define PCI_CMD_MEM_READ 0x06 ++#define PCI_CMD_MEM_WRITE 0x07 ++#define PCI_CMD_CFG_READ 0x0a ++#define PCI_CMD_CFG_WRITE 0x0b ++#define PCI_CMD_MEM_READ_MULT 0x0c /* not supported */ ++#define PCI_CMD_DUAL_ADDR 0x0d /* not supported */ ++#define PCI_CMD_MEM_READ_LINE 0x0e /* not supported */ ++#define PCI_CMD_MEM_WRITE_INVAL 0x0f /* not supported */ ++/* ++ * Status codes, returned by pci_read_u32() and pci_write_u32() ++ */ ++#define PCI_RESP_IN_PROGRESS 0xff /* request still in queue */ ++#define PCI_RESP_OK 0 ++/* ++ * The following codes indicate that the request has completed ++ */ ++#define PCI_RESP_NO_DEVSEL 1 /* timeout before target asserted ++ * DEVSEL! */ ++#define PCI_RESP_LOST_DEVSEL 2 /* had DEVSEL, but went away before ++ * transfer completed! */ ++#define PCI_RESP_BAD_TRDY 3 /* target asserted TRDY without ++ * DEVSEL! */ ++#define PCI_RESP_NO_TRDY 4 /* timeout before target asserted ++ * TRDY! */ ++#define PCI_RESP_BAD_STOP 5 /* target asserted STOP and TRDY ++ * without DEVSEL! */ ++#define PCI_RESP_TARGET_ABORT 6 ++#define PCI_RESP_TARGET_RETRY 7 ++#define PCI_RESP_TARGET_DISCONNECT 8 ++#define PCI_RESP_MISMATCH 9 /* data read back doesn't match data ++ * written - debug only, the core PCI ++ * routines never return this */ ++#define PCI_RESP_DET_SERR 10 ++#define PCI_RESP_DET_PERR 11 ++#define PCI_RESP_MALFORMED_REQ 12 /* Could be due to misaligned ++ * requests or invalid address */ ++#define PCI_RESP_NO_RESOURCE 13 /* Could be memory or other resourse ++ * like queue space */ ++#define PCI_RESP_ERROR 14 /* All emcompassing error */ ++ ++/* registers in PCI config space */ ++#define PCI_DEVICE_VENDOR_ID_REG 0x00 ++#define PCI_STATUS_COMMAND_REG 0x04 ++#define PCI_CLASS_REVISION_REG 0x08 ++#define PCI_BHLC_REG 0x0c /* BIST, Header type, Latency ++ * timer, Cache line size */ ++#define PCI_BASE_ADDR_REG 0x10 ++#define PCI_BASE_REG_COUNT 6 ++#define CARDBUS_CIS_PTR_REG 0x28 ++#define PCI_SUB_SYSTEM_ID_REG 0x2c ++#define PCI_EXP_ROM_ADDR_REG 0x30 ++#define PCI_CAP_PTR_REG 0x34 ++#define PCI_LGPL_REG 0x3C /* max Latency, min Gnt, interrupt ++ * Pin, interrupt Line */ ++ ++struct pci_master_request { ++ volatile u32_t pci_address; /* must be 4-byte aligned */ ++ volatile u32_t data; /* must be 4-byte aligned */ ++ volatile u8_t cmd; ++ volatile u8_t byte_valid; ++ volatile u8_t status; ++}; ++ ++struct pci_devnode { ++ struct devtree_node dn; ++ u32_t pci_idsel_0; ++ u32_t pci_idsel_1; ++ u32_t pci_cpu_address; ++ struct pci_master_request volatile *volatile req; ++}; ++ ++static struct pci_master_request req; /* globally used for faster master write ++ * (discarding result when possible) */ ++static struct pci_devnode *pci_node; ++ ++#if !defined(CONFIG_DEBUG_PCIMEASURE) ++#define PCI_DECLARE_MEASUREMENT ++#define PCI_MEASUREMENT_START() ++#define PCI_MEASUREMENT_END(idx) ++#else ++#define PCI_DECLARE_MEASUREMENT \ ++ int __diff; \ ++ unsigned int __tstart; ++ ++#define PCI_MEASUREMENT_START() \ ++ __tstart = UBICOM32_IO_TIMER->sysval; ++ ++#define PCI_MEASUREMENT_END(idx) \ ++ __diff = (int)UBICOM32_IO_TIMER->sysval - (int)__tstart; \ ++ pci_measurement_update((idx), __diff); ++ ++#define PCI_WEIGHT 32 ++ ++struct pci_measurement { ++ volatile unsigned int min; ++ volatile unsigned int avg; ++ volatile unsigned int max; ++}; ++ ++enum pci_measurement_list { ++ PCI_MEASUREMENT_READ32, ++ PCI_MEASUREMENT_WRITE32, ++ PCI_MEASUREMENT_READ16, ++ PCI_MEASUREMENT_WRITE16, ++ PCI_MEASUREMENT_READ8, ++ PCI_MEASUREMENT_WRITE8, ++ PCI_MEASUREMENT_LAST, ++}; ++ ++static const char *pci_measurement_name_list[PCI_MEASUREMENT_LAST] = { ++ "READ32", ++ "WRITE32", ++ "READ16", ++ "WRITE16", ++ "READ8", ++ "WRITE8" ++}; ++static struct pci_measurement pci_measurements[PCI_MEASUREMENT_LAST]; ++ ++/* ++ * pci_measurement_update() ++ * Update an entry in the measurement array for this idx. ++ */ ++static void pci_measurement_update(int idx, int sample) ++{ ++ struct pci_measurement *pm = &pci_measurements[idx]; ++ if ((pm->min == 0) || (pm->min > sample)) { ++ pm->min = sample; ++ } ++ if (pm->max < sample) { ++ pm->max = sample; ++ } ++ pm->avg = ((pm->avg * (PCI_WEIGHT - 1)) + sample) / PCI_WEIGHT; ++} ++#endif ++ ++#if defined(PCI_USE_INTERNAL_LOCK) ++/* ++ * pci_lock_release() ++ * Release the PCI lock. ++ */ ++static void pci_lock_release(unsigned long irqflag) ++{ ++ UBICOM32_UNLOCK(PCI_LOCK_BIT); ++} ++ ++/* ++ * pci_lock_acquire() ++ * Acquire the PCI lock, spin if not available. ++ */ ++static void pci_lock_acquire(unsigned long irqflag) ++{ ++ UBICOM32_LOCK(PCI_LOCK_BIT); ++} ++#endif ++ ++/* ++ * pci_set_hrt_interrupt() ++ */ ++static inline void pci_set_hrt_interrupt(struct pci_devnode *pci_node) ++{ ++ ubicom32_set_interrupt(pci_node->dn.sendirq); ++} ++ ++/* ++ * pci_read_u32() ++ * Synchronously read 32 bits from PCI space. ++ */ ++u8 pci_read_u32(u8 pci_cmd, u32 address, u32 *data) ++{ ++ u8 status; ++ unsigned long irqflag; ++ ++ ++ /* ++ * Fill in the request. ++ */ ++ volatile struct pci_master_request lreq; ++ PCI_DECLARE_MEASUREMENT; ++ ++ lreq.pci_address = address; ++ lreq.cmd = pci_cmd; ++ lreq.byte_valid = 0xf; /* enable all bytes */ ++ ++ /* ++ * Wait for any previous request to complete and then make this request. ++ */ ++ PCI_MEASUREMENT_START(); ++ PCI_LOCK(&pci_master_lock, irqflag); ++ while (unlikely(pci_node->req == &req)) ++ ; ++ pci_node->req = &lreq; ++ pci_set_hrt_interrupt(pci_node); ++ PCI_UNLOCK(&pci_master_lock, irqflag); ++ ++ /* ++ * Wait for the result to show up. ++ */ ++ while (unlikely(pci_node->req == &lreq)) ++ ; ++ status = lreq.status; ++ if (likely(status == PCI_RESP_OK)) ++ *data = le32_to_cpu(lreq.data); ++ else ++ *data = 0; ++ PCI_MEASUREMENT_END(PCI_MEASUREMENT_READ32); ++ return status; ++} ++ ++/* ++ * pci_write_u32() ++ * Asyncrhnously or synchronously write 32 bits to PCI master space. ++ */ ++u8 pci_write_u32(u8 pci_cmd, u32 address, u32 data) ++{ ++ unsigned long irqflag; ++ PCI_DECLARE_MEASUREMENT; ++ ++ /* ++ * Wait for any previous write or pending read to complete. ++ * ++ * We use a global data block because once we write the request ++ * we do not wait for it to complete before exiting. ++ */ ++ PCI_MEASUREMENT_START(); ++ PCI_LOCK(&pci_master_lock, irqflag); ++ while (unlikely(pci_node->req == &req)) ++ ; ++ req.pci_address = address; ++ req.data = cpu_to_le32(data); ++ req.cmd = pci_cmd; ++ req.byte_valid = 0xf; /* enable all bytes */ ++ pci_node->req = &req; ++ pci_set_hrt_interrupt(pci_node); ++ PCI_UNLOCK(&pci_master_lock, irqflag); ++ PCI_MEASUREMENT_END(PCI_MEASUREMENT_WRITE32); ++ return PCI_RESP_OK; ++} ++ ++/* ++ * pci_read_u16() ++ * Synchronously read 16 bits from PCI space. ++ */ ++u8 pci_read_u16(u8 pci_cmd, u32 address, u16 *data) ++{ ++ u8 status; ++ unsigned long irqflag; ++ ++ /* ++ * Fill in the request. ++ */ ++ volatile struct pci_master_request lreq; ++ PCI_DECLARE_MEASUREMENT; ++ ++ lreq.pci_address = address & ~2; ++ lreq.cmd = pci_cmd; ++ lreq.byte_valid = (address & 2) ? 0xc : 0x3; ++ ++ /* ++ * Wait for any previous request to complete and then make this request. ++ */ ++ PCI_MEASUREMENT_START(); ++ PCI_LOCK(&pci_master_lock, irqflag); ++ while (unlikely(pci_node->req == &req)) ++ ; ++ pci_node->req = &lreq; ++ pci_set_hrt_interrupt(pci_node); ++ PCI_UNLOCK(&pci_master_lock, irqflag); ++ ++ /* ++ * Wait for the result to show up. ++ */ ++ while (unlikely(pci_node->req == &lreq)) ++ ; ++ status = lreq.status; ++ if (likely(status == PCI_RESP_OK)) { ++ lreq.data = le32_to_cpu(lreq.data); ++ *data = (u16)((address & 2) ? (lreq.data >> 16) : lreq.data); ++ } else ++ *data = 0; ++ PCI_MEASUREMENT_END(PCI_MEASUREMENT_READ16); ++ return status; ++} ++ ++/* ++ * pci_write_u16() ++ * Asyncrhnously or synchronously write 16 bits to PCI master space. ++ */ ++u8 pci_write_u16(u8 pci_cmd, u32 address, u16 data) ++{ ++ unsigned long irqflag; ++ PCI_DECLARE_MEASUREMENT; ++ ++ /* ++ * Wait for any previous write or pending read to complete. ++ * ++ * We use a global data block because once we write the request ++ * we do not wait for it to complete before exiting. ++ */ ++ PCI_MEASUREMENT_START(); ++ PCI_LOCK(&pci_master_lock, irqflag); ++ while (unlikely(pci_node->req == &req)) ++ ; ++ req.pci_address = address & ~2; ++ req.data = (u32)data; ++ req.data = cpu_to_le32((address & 2) ? (req.data << 16) : req.data); ++ req.cmd = pci_cmd; ++ req.byte_valid = (address & 2) ? 0xc : 0x3; ++ pci_node->req = &req; ++ pci_set_hrt_interrupt(pci_node); ++ PCI_UNLOCK(&pci_master_lock, irqflag); ++ PCI_MEASUREMENT_END(PCI_MEASUREMENT_WRITE16); ++ return PCI_RESP_OK; ++} ++ ++/* ++ * pci_read_u8() ++ * Synchronously read 8 bits from PCI space. ++ */ ++u8 pci_read_u8(u8 pci_cmd, u32 address, u8 *data) ++{ ++ u8 status; ++ unsigned long irqflag; ++ ++ /* ++ * Fill in the request. ++ */ ++ volatile struct pci_master_request lreq; ++ PCI_DECLARE_MEASUREMENT; ++ ++ lreq.pci_address = address & ~3; ++ lreq.cmd = pci_cmd; ++ lreq.byte_valid = 1 << (address & 0x3); ++ ++ /* ++ * Wait for any previous request to complete and then make this request. ++ */ ++ PCI_MEASUREMENT_START(); ++ PCI_LOCK(&pci_master_lock, irqflag); ++ while (unlikely(pci_node->req == &req)) ++ ; ++ pci_node->req = &lreq; ++ pci_set_hrt_interrupt(pci_node); ++ PCI_UNLOCK(&pci_master_lock, irqflag); ++ ++ /* ++ * Wait for the result to show up. ++ */ ++ while (unlikely(pci_node->req == &lreq)) ++ ; ++ status = lreq.status; ++ if (likely(status == PCI_RESP_OK)) { ++ *data = (u8)(lreq.data >> (24 - ((address & 0x3) << 3))); ++ } else ++ *data = 0; ++ PCI_MEASUREMENT_END(PCI_MEASUREMENT_READ8); ++ return status; ++} ++ ++/* ++ * pci_write_u8() ++ * Asyncrhnously or synchronously write 8 bits to PCI master space. ++ */ ++u8 pci_write_u8(u8 pci_cmd, u32 address, u8 data) ++{ ++ unsigned long irqflag; ++ PCI_DECLARE_MEASUREMENT; ++ ++ /* ++ * Wait for any previous write or pending read to complete. ++ * ++ * We use a global data block because once we write the request ++ * we do not wait for it to complete before exiting. ++ */ ++ PCI_MEASUREMENT_START(); ++ PCI_LOCK(&pci_master_lock, irqflag); ++ while (unlikely(pci_node->req == &req)) ++ ; ++ req.pci_address = address & ~3; ++ req.data = ((u32)data << (24 - ((address & 0x3) << 3))); ++ req.cmd = pci_cmd; ++ req.byte_valid = 1 << (address & 0x3); ++ pci_node->req = &req; ++ pci_set_hrt_interrupt(pci_node); ++ PCI_UNLOCK(&pci_master_lock, irqflag); ++ PCI_MEASUREMENT_END(PCI_MEASUREMENT_WRITE8); ++ return PCI_RESP_OK; ++} ++ ++unsigned int ubi32_pci_read_u32(const volatile void __iomem *addr) ++{ ++ unsigned int data; ++ pci_read_u32(PCI_CMD_MEM_READ, (u32)addr, &data); ++ return data; ++} ++EXPORT_SYMBOL(ubi32_pci_read_u32); ++ ++unsigned short ubi32_pci_read_u16(const volatile void __iomem *addr) ++{ ++ unsigned short data; ++ pci_read_u16(PCI_CMD_MEM_READ, (u32)addr, &data); ++ return data; ++} ++EXPORT_SYMBOL(ubi32_pci_read_u16); ++ ++unsigned char ubi32_pci_read_u8(const volatile void __iomem *addr) ++{ ++ unsigned char data; ++ pci_read_u8(PCI_CMD_MEM_READ, (u32)addr, &data); ++ return data; ++} ++EXPORT_SYMBOL(ubi32_pci_read_u8); ++ ++void ubi32_pci_write_u32(unsigned int val, const volatile void __iomem *addr) ++{ ++ pci_write_u32(PCI_CMD_MEM_WRITE, (u32)addr, val); ++} ++EXPORT_SYMBOL(ubi32_pci_write_u32); ++ ++void ubi32_pci_write_u16(unsigned short val, const volatile void __iomem *addr) ++{ ++ pci_write_u16(PCI_CMD_MEM_WRITE, (u32)addr, val); ++} ++EXPORT_SYMBOL(ubi32_pci_write_u16); ++ ++void ubi32_pci_write_u8(unsigned char val, const void volatile __iomem *addr) ++{ ++ pci_write_u8(PCI_CMD_MEM_WRITE, (u32)addr, val); ++} ++EXPORT_SYMBOL(ubi32_pci_write_u8); ++ ++#if defined(CONFIG_DEBUG_PCIMEASURE) ++static unsigned int pci_cycles_to_nano(unsigned int cycles, unsigned int frequency) ++{ ++ unsigned int nano = ((cycles * 1000) / (frequency / 1000000)); ++ return nano; ++} ++ ++/* ++ * pci_measurement_show() ++ * Print out the min, avg, max values for each PCI transaction type. ++ * ++ * By request, the max value is reset after each dump. ++ */ ++static int pci_measurement_show(struct seq_file *p, void *v) ++{ ++ unsigned int min, avg, max; ++ unsigned int freq = processor_frequency(); ++ int trans = *((loff_t *) v); ++ ++ if (trans == 0) { ++ seq_puts(p, "min\tavg\tmax\t(nano-seconds)\n"); ++ } ++ ++ if (trans >= PCI_MEASUREMENT_LAST) { ++ return 0; ++ } ++ ++ min = pci_cycles_to_nano(pci_measurements[trans].min, freq); ++ avg = pci_cycles_to_nano(pci_measurements[trans].avg, freq); ++ max = pci_cycles_to_nano(pci_measurements[trans].max, freq); ++ pci_measurements[trans].max = 0; ++ seq_printf(p, "%u\t%u\t%u\t%s\n", min, avg, max, pci_measurement_name_list[trans]); ++ return 0; ++} ++ ++static void *pci_measurement_start(struct seq_file *f, loff_t *pos) ++{ ++ return (*pos < PCI_MEASUREMENT_LAST) ? pos : NULL; ++} ++ ++static void *pci_measurement_next(struct seq_file *f, void *v, loff_t *pos) ++{ ++ (*pos)++; ++ if (*pos >= PCI_MEASUREMENT_LAST) ++ return NULL; ++ return pos; ++} ++ ++static void pci_measurement_stop(struct seq_file *f, void *v) ++{ ++ /* Nothing to do */ ++} ++ ++static const struct seq_operations pci_measurement_seq_ops = { ++ .start = pci_measurement_start, ++ .next = pci_measurement_next, ++ .stop = pci_measurement_stop, ++ .show = pci_measurement_show, ++}; ++ ++static int pci_measurement_open(struct inode *inode, struct file *filp) ++{ ++ return seq_open(filp, &pci_measurement_seq_ops); ++} ++ ++static const struct file_operations pci_measurement_fops = { ++ .open = pci_measurement_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++static int __init pci_measurement_init(void) ++{ ++ proc_create("pci_measurements", 0, NULL, &pci_measurement_fops); ++ return 0; ++} ++module_init(pci_measurement_init); ++#endif ++ ++static int ubi32_pci_read_config(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *value) ++{ ++ u8 cmd; ++ u32 addr; ++ u8 data8; ++ u16 data16; ++ ++ u8 slot = PCI_SLOT(devfn); ++ u8 fn = PCI_FUNC(devfn); ++ ++ if (slot > 1) { ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ } else if (slot == 0) { ++ addr = PCI_DEV0_IDSEL + where; ++ } else { ++ addr = PCI_DEV1_IDSEL + where; ++ } ++ ++ addr += (fn << 8); ++ ++ cmd = PCI_CMD_CFG_READ; ++ if (size == 1) { ++ pci_read_u8(cmd, addr, &data8); ++ *value = (u32)data8; ++ } else if (size == 2) { ++ pci_read_u16(cmd, addr, &data16); ++ *value = (u32)data16; ++ } else { ++ pci_read_u32(cmd, addr, value); ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int ubi32_pci_write_config(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 value) ++{ ++ u8 cmd; ++ u32 addr; ++ u8 slot = PCI_SLOT(devfn); ++ u8 fn = PCI_FUNC(devfn); ++ ++ if (slot > 1) { ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ } else if (slot == 0) { ++ addr = PCI_DEV0_IDSEL + where; ++ } else { ++ addr = PCI_DEV1_IDSEL + where; ++ } ++ ++ addr += (fn << 8); ++ ++ cmd = PCI_CMD_CFG_WRITE; ++ if (size == 1) { ++ pci_write_u8(cmd, addr, (u8)value); ++ } else if (size == 2) { ++ pci_write_u16(cmd, addr, (u16)value); ++ } else { ++ pci_write_u32(cmd, addr, value); ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size) ++{ ++ return -EIO; ++} ++EXPORT_SYMBOL(pci_set_dma_max_seg_size); ++ ++int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) ++{ ++ return -EIO; ++} ++EXPORT_SYMBOL(pci_set_dma_seg_boundary); ++ ++void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) ++{ ++ resource_size_t start = pci_resource_start(dev, bar); ++ resource_size_t len = pci_resource_len(dev, bar); ++ unsigned long flags = pci_resource_flags(dev, bar); ++ ++ if (!len || !start) { ++ return NULL; ++ } ++ ++ if (maxlen && len > maxlen) { ++ len = maxlen; ++ } ++ ++ if (flags & IORESOURCE_IO) { ++ return ioport_map(start, len); ++ } ++ ++ if (flags & IORESOURCE_MEM) { ++ if (flags & IORESOURCE_CACHEABLE) { ++ return ioremap(start, len); ++ } ++ return ioremap_nocache(start, len); ++ } ++ return NULL; ++} ++EXPORT_SYMBOL(pci_iomap); ++ ++void pci_iounmap(struct pci_dev *dev, void __iomem *addr) ++{ ++ if ((unsigned long)addr >= VMALLOC_START && ++ (unsigned long)addr < VMALLOC_END) { ++ iounmap(addr); ++ } ++} ++EXPORT_SYMBOL(pci_iounmap); ++ ++/* ++ * From arch/arm/kernel/bios32.c ++ * ++ * PCI bios-type initialisation for PCI machines ++ * ++ * Bits taken from various places. ++ */ ++static void __init pcibios_init_hw(struct hw_pci *hw) ++{ ++ struct pci_sys_data *sys = NULL; ++ int ret; ++ int nr, busnr; ++ ++ for (nr = busnr = 0; nr < hw->nr_controllers; nr++) { ++ sys = kzalloc(sizeof(struct pci_sys_data), GFP_KERNEL); ++ if (!sys) ++ panic("PCI: unable to allocate sys data!"); ++ ++ sys->hw = hw; ++ sys->busnr = busnr; ++ sys->map_irq = hw->map_irq; ++ sys->resource[0] = &ioport_resource; ++ sys->resource[1] = &iomem_resource; ++ ++ ret = hw->setup(nr, sys); ++ ++ if (ret > 0) { ++ sys->bus = hw->scan(nr, sys); ++ ++ if (!sys->bus) ++ panic("PCI: unable to scan bus!"); ++ ++ busnr = sys->bus->subordinate + 1; ++ ++ list_add(&sys->node, &hw->buses); ++ } else { ++ kfree(sys); ++ if (ret < 0) ++ break; ++ } ++ } ++} ++ ++/* ++ * Swizzle the device pin each time we cross a bridge. ++ * This might update pin and returns the slot number. ++ */ ++static u8 __devinit pcibios_swizzle(struct pci_dev *dev, u8 *pin) ++{ ++ struct pci_sys_data *sys = dev->sysdata; ++ int slot = 0, oldpin = *pin; ++ ++ if (sys->swizzle) ++ slot = sys->swizzle(dev, pin); ++ ++ if (debug_pci) ++ printk("PCI: %s swizzling pin %d => pin %d slot %d\n", ++ pci_name(dev), oldpin, *pin, slot); ++ return slot; ++} ++ ++/* ++ * Map a slot/pin to an IRQ. ++ */ ++static int pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ struct pci_sys_data *sys = dev->sysdata; ++ int irq = -1; ++ ++ if (sys->map_irq) ++ irq = sys->map_irq(dev, slot, pin); ++ ++ if (debug_pci) ++ printk("PCI: %s mapping slot %d pin %d => irq %d\n", ++ pci_name(dev), slot, pin, irq); ++ ++ return irq; ++} ++ ++void __init pci_common_init(struct hw_pci *hw) ++{ ++ struct pci_sys_data *sys; ++ ++ INIT_LIST_HEAD(&hw->buses); ++ ++ if (hw->preinit) ++ hw->preinit(); ++ pcibios_init_hw(hw); ++ if (hw->postinit) ++ hw->postinit(); ++ ++ pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq); ++ list_for_each_entry(sys, &hw->buses, node) { ++ struct pci_bus *bus = sys->bus; ++ /* ++ * Size the bridge windows. ++ */ ++ pci_bus_size_bridges(bus); ++ /* ++ * Assign resources. ++ */ ++ pci_bus_assign_resources(bus); ++ ++ /* ++ * Tell drivers about devices found. ++ */ ++ pci_bus_add_devices(bus); ++ } ++} ++ ++char * __init pcibios_setup(char *str) ++{ ++ if (!strcmp(str, "debug")) { ++ debug_pci = 1; ++ return NULL; ++ } ++ return str; ++} ++ ++/* ++ * From arch/i386/kernel/pci-i386.c: ++ * ++ * We need to avoid collisions with `mirrored' VGA ports ++ * and other strange ISA hardware, so we always want the ++ * addresses to be allocated in the 0x000-0x0ff region ++ * modulo 0x400. ++ * ++ * Why? Because some silly external IO cards only decode ++ * the low 10 bits of the IO address. The 0x00-0xff region ++ * is reserved for motherboard devices that decode all 16 ++ * bits, so it's ok to allocate at, say, 0x2800-0x28ff, ++ * but we want to try to avoid allocating at 0x2900-0x2bff ++ * which might be mirrored at 0x0100-0x03ff.. ++ */ ++void pcibios_align_resource(void *data, struct resource *res, ++ resource_size_t size, resource_size_t align) ++{ ++ resource_size_t start = res->start; ++ ++ if (res->flags & IORESOURCE_IO && start & 0x300) ++ start = (start + 0x3ff) & ~0x3ff; ++ ++ res->start = (start + align - 1) & ~(align - 1); ++} ++ ++ ++void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) ++{ ++ if (debug_pci) ++ printk("PCI: Assigning IRQ %02d to %s\n", irq, pci_name(dev)); ++ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); ++} ++ ++/* ++ * If the bus contains any of these devices, then we must not turn on ++ * parity checking of any kind. Currently this is CyberPro 20x0 only. ++ */ ++static inline int pdev_bad_for_parity(struct pci_dev *dev) ++{ ++ return (dev->vendor == PCI_VENDOR_ID_INTERG && ++ (dev->device == PCI_DEVICE_ID_INTERG_2000 || ++ dev->device == PCI_DEVICE_ID_INTERG_2010)) || ++ (dev->vendor == PCI_VENDOR_ID_ITE && ++ dev->device == PCI_DEVICE_ID_ITE_8152); ++ ++} ++ ++/* ++ * Adjust the device resources from bus-centric to Linux-centric. ++ */ ++static void __devinit ++pdev_fixup_device_resources(struct pci_sys_data *root, struct pci_dev *dev) ++{ ++ resource_size_t offset; ++ int i; ++ ++ for (i = 0; i < PCI_NUM_RESOURCES; i++) { ++ if (dev->resource[i].start == 0) ++ continue; ++ if (dev->resource[i].flags & IORESOURCE_MEM) ++ offset = root->mem_offset; ++ else ++ offset = root->io_offset; ++ ++ dev->resource[i].start += offset; ++ dev->resource[i].end += offset; ++ } ++} ++ ++static void __devinit ++pbus_assign_bus_resources(struct pci_bus *bus, struct pci_sys_data *root) ++{ ++ struct pci_dev *dev = bus->self; ++ int i; ++ ++ if (!dev) { ++ /* ++ * Assign root bus resources. ++ */ ++ for (i = 0; i < 3; i++) ++ bus->resource[i] = root->resource[i]; ++ } ++} ++ ++/* ++ * pcibios_fixup_bus - Called after each bus is probed, ++ * but before its children are examined. ++ */ ++void pcibios_fixup_bus(struct pci_bus *bus) ++{ ++ struct pci_sys_data *root = bus->sysdata; ++ struct pci_dev *dev; ++ u16 features = PCI_COMMAND_SERR | PCI_COMMAND_PARITY | ++ PCI_COMMAND_FAST_BACK; ++ ++ pbus_assign_bus_resources(bus, root); ++ ++ /* ++ * Walk the devices on this bus, working out what we can ++ * and can't support. ++ */ ++ list_for_each_entry(dev, &bus->devices, bus_list) { ++ u16 status; ++ ++ pdev_fixup_device_resources(root, dev); ++ ++ pci_read_config_word(dev, PCI_STATUS, &status); ++ ++ /* ++ * If any device on this bus does not support fast back ++ * to back transfers, then the bus as a whole is not able ++ * to support them. Having fast back to back transfers ++ * on saves us one PCI cycle per transaction. ++ */ ++ if (!(status & PCI_STATUS_FAST_BACK)) ++ features &= ~PCI_COMMAND_FAST_BACK; ++ ++ if (pdev_bad_for_parity(dev)) ++ features &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); ++ ++ switch (dev->class >> 8) { ++ case PCI_CLASS_BRIDGE_PCI: ++ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &status); ++ status |= PCI_BRIDGE_CTL_PARITY | ++ PCI_BRIDGE_CTL_MASTER_ABORT; ++ status &= ~(PCI_BRIDGE_CTL_BUS_RESET | ++ PCI_BRIDGE_CTL_FAST_BACK); ++ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, status); ++ break; ++ ++ case PCI_CLASS_BRIDGE_CARDBUS: ++ pci_read_config_word(dev, PCI_CB_BRIDGE_CONTROL, ++ &status); ++ status |= PCI_CB_BRIDGE_CTL_PARITY | ++ PCI_CB_BRIDGE_CTL_MASTER_ABORT; ++ pci_write_config_word(dev, PCI_CB_BRIDGE_CONTROL, ++ status); ++ break; ++ } ++ } ++ ++ /* ++ * Now walk the devices again, this time setting them up. ++ */ ++ list_for_each_entry(dev, &bus->devices, bus_list) { ++ u16 cmd; ++ ++ pci_read_config_word(dev, PCI_COMMAND, &cmd); ++ cmd |= features; ++ pci_write_config_word(dev, PCI_COMMAND, cmd); ++ ++ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, ++ L1_CACHE_BYTES >> 2); ++ } ++ ++ /* ++ * Propagate the flags to the PCI bridge. ++ */ ++ if (bus->self && bus->self->hdr_type == PCI_HEADER_TYPE_BRIDGE) { ++ if (features & PCI_COMMAND_FAST_BACK) ++ bus->bridge_ctl |= PCI_BRIDGE_CTL_FAST_BACK; ++ if (features & PCI_COMMAND_PARITY) ++ bus->bridge_ctl |= PCI_BRIDGE_CTL_PARITY; ++ } ++ ++ /* ++ * Report what we did for this bus ++ */ ++ printk(KERN_INFO "PCI: bus%d: Fast back to back transfers %sabled\n", ++ bus->number, (features & PCI_COMMAND_FAST_BACK) ? "en" : "dis"); ++} ++/* ++ * Convert from Linux-centric to bus-centric addresses for bridge devices. ++ */ ++void ++pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, ++ struct resource *res) ++{ ++ struct pci_sys_data *root = dev->sysdata; ++ unsigned long offset = 0; ++ ++ if (res->flags & IORESOURCE_IO) ++ offset = root->io_offset; ++ if (res->flags & IORESOURCE_MEM) ++ offset = root->mem_offset; ++ ++ region->start = res->start - offset; ++ region->end = res->end - offset; ++} ++ ++void __devinit ++pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, ++ struct pci_bus_region *region) ++{ ++ struct pci_sys_data *root = dev->sysdata; ++ unsigned long offset = 0; ++ ++ if (res->flags & IORESOURCE_IO) ++ offset = root->io_offset; ++ if (res->flags & IORESOURCE_MEM) ++ offset = root->mem_offset; ++ ++ res->start = region->start + offset; ++ res->end = region->end + offset; ++} ++ ++#ifdef CONFIG_HOTPLUG ++EXPORT_SYMBOL(pcibios_fixup_bus); ++EXPORT_SYMBOL(pcibios_resource_to_bus); ++EXPORT_SYMBOL(pcibios_bus_to_resource); ++#endif ++ ++/** ++ * pcibios_enable_device - Enable I/O and memory. ++ * @dev: PCI device to be enabled ++ */ ++int pcibios_enable_device(struct pci_dev *dev, int mask) ++{ ++ u16 cmd, old_cmd; ++ int idx; ++ struct resource *r; ++ ++ pci_read_config_word(dev, PCI_COMMAND, &cmd); ++ old_cmd = cmd; ++ for (idx = 0; idx < 6; idx++) { ++ /* Only set up the requested stuff */ ++ if (!(mask & (1 << idx))) ++ continue; ++ ++ r = dev->resource + idx; ++ if (!r->start && r->end) { ++ printk(KERN_ERR "PCI: Device %s not available because" ++ " of resource collisions\n", pci_name(dev)); ++ return -EINVAL; ++ } ++ if (r->flags & IORESOURCE_IO) ++ cmd |= PCI_COMMAND_IO; ++ if (r->flags & IORESOURCE_MEM) ++ cmd |= PCI_COMMAND_MEMORY; ++ } ++ ++ /* ++ * Bridges (eg, cardbus bridges) need to be fully enabled ++ */ ++ if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) ++ cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY; ++ ++ if (cmd != old_cmd) { ++ printk("PCI: enabling device %s (%04x -> %04x)\n", ++ pci_name(dev), old_cmd, cmd); ++ pci_write_config_word(dev, PCI_COMMAND, cmd); ++ } ++ return 0; ++} ++ ++ ++struct pci_ops ubi32_pci_ops = { ++ .read = ubi32_pci_read_config, ++ .write = ubi32_pci_write_config, ++}; ++ ++static struct pci_bus *ubi32_pci_scan_bus(int nr, struct pci_sys_data *sys) ++{ ++ return pci_scan_bus(sys->busnr, &ubi32_pci_ops, sys); ++} ++ ++#define UBI32_PCI_MEM_BASE PCI_DEV_REG_BASE ++#define UBI32_PCI_MEM_LEN 0x80000000 ++ ++#define UBI32_PCI_IO_BASE 0x0 ++#define UBI32_PCI_IO_END 0x0 ++ ++static struct resource ubi32_pci_mem = { ++ .name = "PCI memory space", ++ .start = UBI32_PCI_MEM_BASE, ++ .end = UBI32_PCI_MEM_BASE + UBI32_PCI_MEM_LEN - 1, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct resource ubi32_pci_io = { ++ .name = "PCI IO space", ++ .start = UBI32_PCI_IO_BASE, ++ .end = UBI32_PCI_IO_END, ++ .flags = IORESOURCE_IO, ++}; ++ ++static int __init ubi32_pci_setup(int nr, struct pci_sys_data *sys) ++{ ++ if (nr > 0) ++ return 0; ++ ++ request_resource(&iomem_resource, &ubi32_pci_mem); ++ request_resource(&ioport_resource, &ubi32_pci_io); ++ ++ sys->resource[0] = &ubi32_pci_io; ++ sys->resource[1] = &ubi32_pci_mem; ++ sys->resource[2] = NULL; ++ ++ return 1; ++} ++ ++static void __init ubi32_pci_preinit(void) ++{ ++} ++ ++static int __init ubi32_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ return pci_node->dn.recvirq; ++} ++ ++struct hw_pci ubi32_pci __initdata = { ++ .nr_controllers = 1, ++ .preinit = ubi32_pci_preinit, ++ .setup = ubi32_pci_setup, ++ .scan = ubi32_pci_scan_bus, ++ .map_irq = ubi32_pci_map_irq, ++}; ++ ++static int __init ubi32_pci_init(void) ++{ ++ pci_node = (struct pci_devnode *)devtree_find_node("pci"); ++ if (pci_node == NULL) { ++ printk(KERN_WARNING "PCI init failed\n"); ++ return -ENOSYS; ++ } ++ pci_common_init(&ubi32_pci); ++ return 0; ++} ++ ++subsys_initcall(ubi32_pci_init); ++ ++/* ++ * workaround for dual PCI card interrupt ++ */ ++#define PCI_COMMON_INT_BIT (1 << 19) ++void ubi32_pci_int_wr(void) ++{ ++ volatile unsigned int pci_int_line; ++ pci_int_line = UBICOM32_IO_PORT(RB)->gpio_in; ++ if (!(pci_int_line & PCI_COMMON_INT_BIT)) ++ { ++ ubicom32_set_interrupt(pci_node->dn.recvirq); ++ } ++} ++EXPORT_SYMBOL(ubi32_pci_int_wr); +--- /dev/null ++++ b/arch/ubicom32/mach-common/ring_tio.c +@@ -0,0 +1,123 @@ ++/* ++ * arch/ubicom32/mach-common/ring_tio.c ++ * Generic initialization for UIO Ubicom32 Ring ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/types.h> ++ ++#include <asm/devtree.h> ++#include <asm/ring_tio.h> ++ ++static const char *ring_tio_driver_name = "uio_ubicom32ring"; ++ ++/* ++ * The number of ring_tio's currently allocated, used for .id ++ */ ++static int __initdata ring_tio_count; ++ ++/* ++ * The maximum number of resources that the ring_tio will have. ++ * Currently 3, a register space, and up to 2 interrupts. ++ */ ++#define RING_TIO_MAX_RESOURCES 3 ++ ++/* ++ * ring_tio_init ++ * Checks the device tree and instantiates the driver if found ++ */ ++void __init ring_tio_init(const char *node_name) ++{ ++ struct platform_device *pdev; ++ struct resource *res; ++ int resource_idx = 0; ++ struct ring_tio_node *ring_node; ++ ++ /* ++ * Check the device tree for the ring_tio ++ */ ++ ring_node = (struct ring_tio_node *)devtree_find_node(node_name); ++ if (!ring_node) { ++ printk(KERN_WARNING "Ring TIO '%s' not found\n", node_name); ++ return; ++ } ++ ++ if (ring_node->version != RING_TIO_NODE_VERSION) { ++ printk(KERN_WARNING "ring_tio not compatible\n"); ++ return; ++ } ++ ++ /* ++ * Dynamically create the platform_device structure and resources ++ */ ++ pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); ++ if (!pdev) { ++ printk(KERN_WARNING "ring_tio could not alloc pdev\n"); ++ return; ++ } ++ ++ res = kzalloc(sizeof(struct resource) * RING_TIO_MAX_RESOURCES, ++ GFP_KERNEL); ++ if (!res) { ++ kfree(pdev); ++ printk(KERN_WARNING "ring_tio could not alloc res\n"); ++ return; ++ } ++ ++ pdev->name = ring_tio_driver_name; ++ pdev->id = ring_tio_count++; ++ pdev->resource = res; ++ ++ /* ++ * Fill in the resources and platform data from devtree information ++ */ ++ res[resource_idx].start = (u32_t)(ring_node->regs); ++ res[resource_idx].end = (u32_t)(ring_node->regs); ++ res[resource_idx].flags = IORESOURCE_MEM; ++ resource_idx++; ++ ++ if (ring_node->dn.sendirq != 0xFF) { ++ res[resource_idx].start = ring_node->dn.sendirq; ++ res[resource_idx].flags = IORESOURCE_IRQ; ++ resource_idx++; ++ } ++ ++ if (ring_node->dn.recvirq != 0xFF) { ++ res[resource_idx].start = ring_node->dn.recvirq; ++ res[resource_idx].flags = IORESOURCE_IRQ; ++ resource_idx++; ++ } ++ pdev->num_resources = resource_idx; ++ ++ printk(KERN_INFO "RingTIO.%d '%s' found irq=%d/%d regs=%p pdev=%p/%p\n", ++ ring_tio_count - 1, node_name, ring_node->dn.sendirq, ++ ring_node->dn.recvirq, ring_node->regs, pdev, res); ++ ++ /* ++ * Try to get the device registered ++ */ ++ pdev->dev.platform_data = (void *)node_name; ++ if (platform_device_register(pdev) < 0) { ++ printk(KERN_WARNING "Ring failed to register\n"); ++ kfree(pdev); ++ kfree(res); ++ } ++} +--- /dev/null ++++ b/arch/ubicom32/mach-common/ubi32-gpio.c +@@ -0,0 +1,411 @@ ++/* ++ * arch/ubicom32/mach-common/ubi32-gpio.c ++ * Ubicom gpio driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/io.h> ++#include <linux/gpio.h> ++#include <linux/irq.h> ++#include <linux/version.h> ++ ++#if defined(CONFIG_PROC_FS) ++#include <linux/proc_fs.h> ++#endif ++ ++#include <linux/io.h> ++#include <asm/ip5000.h> ++#include <linux/gpio.h> ++ ++#define UBI_GPIO_CHECK_RANGE 0 /* !0 enables range checking */ ++ ++ ++/* ++ * Each I/O port can be configured to operate in one of several ++ * functional modes. One of these modes is GPIO, which causes the ++ * entire port to function as a GPIO port. Since the various port ++ * registers serve the system with other important functions, such as ++ * ethernet, serial, USB, etc., it isn't advantageous to set any of ++ * the ports to be entirely dedicated for GPIO use. The processor ++ * alternatively allows individual bits of a port to be assigned to be ++ * used as GPIO independently from the overall port function. This ++ * bit-by-bit assignment is selected by setting the corresponding bit ++ * in the port's gpio_mask register. When set, the selected bit is ++ * then enabled as a GPIO. If the corresponding bit is set in the ++ * gpio_ctl register of the port, the bit is configured as a GPIO ++ * output. Otherwise, it is an input. ++ * ++ * NOTE: This driver uses the bit-by-bit GPIO function assignment ++ * exclusively and *never* sets the port function registers to the ++ * GPIO function. ++ * ++ * GPIO is not the main function of any of the I/O ports. The port ++ * bit widths are variable from one port to the next, determined by ++ * the more common I/O functions of the ports. For simplicity, this ++ * driver assumes all the ports are 32 bits wide regardless of the ++ * real bit width of the port. GPIO bits are numbered from zero to ++ * MAX_UBICOM_GPIOS. Within a port, the least significant bit is ++ * numbered bit zero, the most significant is bit 31. Since the ports ++ * are considered logically contiguous, GPIO #32 is the zeroth bit in ++ * port #1, and so on. Due to the hardware definition, there are ++ * large gaps in the GPIO numbers representing real pins. ++ * ++ * NOTE: It is up to the programmer to refer to the processor data ++ * sheet to determine which bits in which ports can be accessed and ++ * used for GPIO. ++ * ++ */ ++ ++ ++/* There are 9 ports, A through I. Not all 32 bits in each ++ * port can be a GPIO, but we pretend they are. Its up to the ++ * programmer to refer to the processor data sheet. ++ */ ++#define MAX_UBICOM_GPIOS (9 * 32) /* ARCH_NR_GPIOS */ ++#define NUM_GPIO_PORTS (gpio_bank(MAX_UBICOM_GPIOS)) ++ ++ ++/* GPIO reservation bit map array */ ++static int reserved_gpio_map[NUM_GPIO_PORTS]; ++ ++ ++/* Array of hardware io_port addresses */ ++static struct ubicom32_io_port *gpio_bank_addr[NUM_GPIO_PORTS] = ++{ ++ UBICOM32_IO_PORT(RA), ++ UBICOM32_IO_PORT(RB), ++ UBICOM32_IO_PORT(RC), ++ UBICOM32_IO_PORT(RD), ++ UBICOM32_IO_PORT(RE), ++ UBICOM32_IO_PORT(RF), ++ UBICOM32_IO_PORT(RG), ++ UBICOM32_IO_PORT(RH), ++ UBICOM32_IO_PORT(RI) ++}; ++ ++ ++struct ubi_gpio_chip { ++ /* ++ * Right now, nothing else lives here. ++ */ ++ struct gpio_chip gpio_chip; ++}; ++ ++ ++#if UBI_GPIO_CHECK_RANGE ++inline int check_gpio(unsigned gpio) ++{ ++ if (gpio >= MAX_UBICOM_GPIOS) ++ return -EINVAL; ++ return 0; ++} ++#else ++#define check_gpio(n) (0) ++#endif ++ ++/* ++ * ubi_gpio_get_port ++ * Get the IO port associated with a certain gpio ++ */ ++struct ubicom32_io_port *ubi_gpio_get_port(unsigned gpio) ++{ ++ if (gpio_bank(gpio) > NUM_GPIO_PORTS) { ++ return NULL; ++ } ++ return gpio_bank_addr[gpio_bank(gpio)]; ++} ++ ++/* ++ * ubi_gpio_error() ++ */ ++static void ubi_gpio_error(unsigned gpio) ++{ ++ printk(KERN_ERR "ubicom-gpio: GPIO %d wasn't requested!\n", gpio); ++} ++ ++/* ++ * ubi_port_setup() ++ */ ++static void ubi_port_setup(unsigned gpio, unsigned short usage) ++{ ++ if (!check_gpio(gpio)) { ++ if (usage) { ++ UBICOM32_GPIO_ENABLE(gpio); ++ } else { ++ UBICOM32_GPIO_DISABLE(gpio); ++ } ++ } ++} ++ ++/* ++ * ubi_gpio_request() ++ */ ++static int ubi_gpio_request(struct gpio_chip *chip, unsigned gpio) ++{ ++ unsigned long flags; ++ ++ if (check_gpio(gpio) < 0) ++ return -EINVAL; ++ ++ local_irq_save(flags); ++ ++ if (unlikely(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { ++ printk(KERN_ERR "ubi-gpio: GPIO %d is already reserved!\n", ++ gpio); ++ local_irq_restore(flags); ++ return -EBUSY; ++ } ++ ++ reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio); ++ ++ ubi_port_setup(gpio, 1); ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/* ++ * ubi_gpio_free() ++ */ ++static void ubi_gpio_free(struct gpio_chip *chip, unsigned gpio) ++{ ++ unsigned long flags; ++ ++ if (check_gpio(gpio) < 0) ++ return; ++ ++ local_irq_save(flags); ++ ++ if (unlikely(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)))) { ++ ubi_gpio_error(gpio); ++ local_irq_restore(flags); ++ return; ++ } ++ ++ /* Assert the pin is no longer claimed */ ++ reserved_gpio_map[gpio_bank(gpio)] &= ~gpio_bit(gpio); ++ ++ /* Revert port bit to use specified by port->function */ ++ ubi_port_setup(gpio, 0); ++ ++ local_irq_restore(flags); ++} ++ ++/* ++ * ubi_gpio_direction_input() ++ */ ++static int ubi_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) ++{ ++ unsigned long flags; ++ ++ if (!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { ++ ubi_gpio_error(gpio); ++ return -EINVAL; ++ } ++ ++ local_irq_save(flags); ++ ++ /* Configure pin as gpio */ ++ ubi_port_setup(gpio, 1); ++ ++ /* Assert pin is an input */ ++ UBICOM32_GPIO_SET_PIN_INPUT(gpio); ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++ ++/* ++ * ubi_gpio_direction_output() ++ */ ++static int ubi_gpio_direction_output(struct gpio_chip *chip, ++ unsigned gpio, int value) ++{ ++ unsigned long flags; ++ ++ if (!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { ++ ubi_gpio_error(gpio); ++ return -EINVAL; ++ } ++ ++ local_irq_save(flags); ++ ++ /* Configure pin as gpio and set initial value in gpio_out register ++ * so that when we enable it as an output, it will have the correct ++ * initial value. ++ */ ++ ubi_port_setup(gpio, 1); ++ if (value) { ++ UBICOM32_GPIO_SET_PIN_HIGH(gpio); ++ } else { ++ UBICOM32_GPIO_SET_PIN_LOW(gpio); ++ } ++ ++ /* Enable the pin as an output */ ++ UBICOM32_GPIO_SET_PIN_OUTPUT(gpio); ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++ ++/* ++ * ubi_gpio_get_value() ++ */ ++static int ubi_gpio_get_value(struct gpio_chip *chip, unsigned gpio) ++{ ++ return 0 != (gpio_bank_addr[gpio_bank(gpio)]->gpio_in & gpio_bit(gpio)); ++} ++ ++ ++/* ++ * ubi_gpio_set_value() ++ */ ++static void ubi_gpio_set_value(struct gpio_chip *chip, unsigned gpio, ++ int arg) ++{ ++ unsigned long flags; ++ local_irq_save(flags); ++ ++ if (arg) { ++ UBICOM32_GPIO_SET_PIN_HIGH(gpio); ++ } else { ++ UBICOM32_GPIO_SET_PIN_LOW(gpio); ++ } ++ ++ local_irq_restore(flags); ++} ++ ++ ++/* ++ * ubi_gpio_to_irq() ++ */ ++static int ubi_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) ++{ ++ return gpio_to_irq(gpio); ++} ++ ++ ++/* ++ * ubi_gpio_init() ++ */ ++int __init ubi_gpio_init(void) ++{ ++ int k; ++ int status; ++ struct ubi_gpio_chip *chip; ++ struct gpio_chip *gc; ++ ++ printk(KERN_INFO "Ubicom GPIO Controller\n"); ++ ++ chip = kzalloc(sizeof(struct ubi_gpio_chip), GFP_KERNEL); ++ if (chip == NULL) ++ return -ENOMEM; ++ ++ gc = &chip->gpio_chip; ++ gc->request = ubi_gpio_request; ++ gc->free = ubi_gpio_free; ++ gc->to_irq = ubi_gpio_to_irq; ++ gc->direction_input = ubi_gpio_direction_input; ++ gc->direction_output = ubi_gpio_direction_output; ++ gc->get = ubi_gpio_get_value; ++ gc->set = ubi_gpio_set_value; ++ gc->can_sleep = 0; ++ gc->base = 0; ++ gc->ngpio = MAX_UBICOM_GPIOS; /* ARCH_NR_GPIOS - 1 */ ++ gc->label = "ubi_gpio"; ++ ++ status = gpiochip_add(gc); ++ if (status != 0) { ++ kfree(chip); ++ return status; ++ } ++ ++ /* Assert all pins are free */ ++ for (k = 0; k < NUM_GPIO_PORTS; k++) { ++ reserved_gpio_map[k] = 0; ++ } ++ ++ return 0; ++} ++ ++#if defined(CONFIG_PROC_FS) ++/* ++ * ubi_get_gpio_dir() ++ */ ++static int ubi_get_gpio_dir(unsigned gpio) ++{ ++ if (gpio_bank_addr[gpio_bank(gpio)]->gpio_ctl & gpio_bit(gpio)) ++ return 1; ++ else ++ return 0; ++} ++ ++/* ++ * gpio_proc_read() ++ */ ++static int ubi_gpio_proc_read(char *buf, char **start, off_t offset, ++ int len, int *unused_i, void *unused_v) ++{ ++ int c, outlen = 0; ++ ++ for (c = 0; c < MAX_UBICOM_GPIOS; c++) { ++ if (!check_gpio(c) && ++ (reserved_gpio_map[gpio_bank(c)] & gpio_bit(c))) { ++ len = sprintf(buf, "GPIO_%d:\t\tGPIO %s\n", c, ++ ubi_get_gpio_dir(c) ? "OUTPUT" : "INPUT"); ++ } else { ++ continue; ++ } ++ ++ buf += len; ++ outlen += len; ++ } ++ return outlen; ++} ++ ++/* ++ * ubi_gpio_register_proc() ++ */ ++static __init int ubi_gpio_register_proc(void) ++{ ++ struct proc_dir_entry *proc_gpio; ++ ++ proc_gpio = create_proc_entry("gpio", S_IRUGO, NULL); ++ if (proc_gpio) ++ proc_gpio->read_proc = ubi_gpio_proc_read; ++ ++ return proc_gpio != NULL; ++} ++device_initcall(ubi_gpio_register_proc); ++#endif +--- /dev/null ++++ b/arch/ubicom32/mach-common/ubicom32hid.c +@@ -0,0 +1,557 @@ ++/* ++ * arch/ubicom32/mach-common/ubicom32hid.c ++ * I2C driver for HID coprocessor found on some DPF implementations. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/gpio.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/i2c.h> ++#include <linux/backlight.h> ++#include <linux/fb.h> ++#include <linux/input.h> ++#include <linux/input-polldev.h> ++ ++#include <asm/ubicom32hid.h> ++ ++#define DRIVER_NAME "ubicom32hid" ++ ++#ifdef DEBUG ++static int ubicom32hid_debug; ++#endif ++ ++static const struct i2c_device_id ubicom32hid_id[] = { ++ { DRIVER_NAME, }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ubicom32hid_id); ++ ++/* ++ * Define this to make IR checking strict, in general, it's not needed ++ */ ++#undef UBICOM32HID_STRICT_IR_CHECK ++ ++#define UBICOM32HID_CMD_SET_PWM 0x01 ++#define UBICOM32HID_CMD_SET_BL_EN 0x02 ++#define UBICOM32HID_BL_EN_LOW 0x00 ++#define UBICOM32HID_BL_EN_HIZ 0x01 ++#define UBICOM32HID_BL_EN_HI 0x02 ++#define UBICOM32HID_CMD_FLUSH 0x99 ++#define UBICOM32HID_CMD_RESET 0x99 ++#define UBICOM32HID_CMD_GET_IR_SWITCH 0xC0 ++#define UBICOM32HID_CMD_GET_REVISION 0xfd ++#define UBICOM32HID_CMD_GET_DEVICE_ID 0xfe ++#define UBICOM32HID_CMD_GET_VERSION 0xff ++#define UBICOM32HID_DEVICE_ID 0x49 ++ ++#define UBICOM32HID_MAX_BRIGHTNESS_PWM 255 ++ ++/* ++ * Data structure returned by the HID device ++ */ ++struct ubicom32hid_input_data { ++ uint32_t ircmd; ++ uint8_t sw_state; ++ uint8_t sw_changed; ++}; ++ ++/* ++ * Our private data ++ */ ++struct ubicom32hid_data { ++ /* ++ * Pointer to the platform data structure, we need the settings. ++ */ ++ const struct ubicom32hid_platform_data *pdata; ++ ++ /* ++ * Backlight device ++ */ ++ struct backlight_device *bldev; ++ ++ /* ++ * I2C client, for sending messages to the HID device ++ */ ++ struct i2c_client *client; ++ ++ /* ++ * Current intensity, used for get_intensity. ++ */ ++ int cur_intensity; ++ ++ /* ++ * Input subsystem ++ * We won't register an input subsystem if there are no mappings. ++ */ ++ struct input_polled_dev *poll_dev; ++}; ++ ++ ++/* ++ * ubicom32hid_set_intensity ++ */ ++static int ubicom32hid_set_intensity(struct backlight_device *bd) ++{ ++ struct ubicom32hid_data *ud = ++ (struct ubicom32hid_data *)bl_get_data(bd); ++ int intensity = bd->props.brightness; ++ int reg; ++ u8_t val; ++ int ret; ++ ++ /* ++ * If we're blanked the the intensity doesn't matter. ++ */ ++ if ((bd->props.power != FB_BLANK_UNBLANK) || ++ (bd->props.fb_blank != FB_BLANK_UNBLANK)) { ++ intensity = 0; ++ } ++ ++ /* ++ * Set the brightness based on the type of backlight ++ */ ++ if (ud->pdata->type == UBICOM32HID_BL_TYPE_BINARY) { ++ reg = UBICOM32HID_CMD_SET_BL_EN; ++ if (intensity) { ++ val = ud->pdata->invert ++ ? UBICOM32HID_BL_EN_LOW : UBICOM32HID_BL_EN_HI; ++ } else { ++ val = ud->pdata->invert ++ ? UBICOM32HID_BL_EN_HI : UBICOM32HID_BL_EN_LOW; ++ } ++ } else { ++ reg = UBICOM32HID_CMD_SET_PWM; ++ val = ud->pdata->invert ++ ? (UBICOM32HID_MAX_BRIGHTNESS_PWM - intensity) : ++ intensity; ++ } ++ ++ /* ++ * Send the command ++ */ ++ ret = i2c_smbus_write_byte_data(ud->client, reg, val); ++ if (ret < 0) { ++ dev_warn(&ud->client->dev, "Unable to write backlight err=%d\n", ++ ret); ++ return ret; ++ } ++ ++ ud->cur_intensity = intensity; ++ ++ return 0; ++} ++ ++/* ++ * ubicom32hid_get_intensity ++ * Return the current intensity of the backlight. ++ */ ++static int ubicom32hid_get_intensity(struct backlight_device *bd) ++{ ++ struct ubicom32hid_data *ud = ++ (struct ubicom32hid_data *)bl_get_data(bd); ++ ++ return ud->cur_intensity; ++} ++ ++/* ++ * ubicom32hid_verify_data ++ * Verify the data to see if there is any action to be taken ++ * ++ * Returns 0 if no action is to be taken, non-zero otherwise ++ */ ++static int ubicom32hid_verify_data(struct ubicom32hid_data *ud, ++ struct ubicom32hid_input_data *data) ++{ ++ uint8_t *ircmd = (uint8_t *)&(data->ircmd); ++ ++ /* ++ * ircmd == DEADBEEF means ir queue is empty. Since this is a ++ * meaningful code, that means the rest of the message is most likely ++ * correct, so only process the data if the switch state has changed. ++ */ ++ if (data->ircmd == 0xDEADBEEF) { ++ return data->sw_changed != 0; ++ } ++ ++ /* ++ * We have an ircmd which is not empty: ++ * Data[1] should be the complement of Data[0] ++ */ ++ if (ircmd[0] != (u8_t)~ircmd[1]) { ++ return 0; ++ } ++ ++#ifdef UBICOM32HID_STRICT_IR_CHECK ++ /* ++ * It seems that some remote controls don't follow the NEC protocol ++ * properly, so only do this check if the remote does indeed follow the ++ * spec. Data[3] should be the complement of Data[2] ++ */ ++ if (ircmd[2] == (u8_t)~ircmd[3]) { ++ return 1; ++ } ++ ++ /* ++ * For non-compliant remotes, check the system code according to what ++ * they send. ++ */ ++ if ((ircmd[2] != UBICOM32HID_IR_SYSTEM_CODE_CHECK) || ++ (ircmd[3] != UBICOM32HID_IR_SYSTEM_CODE)) { ++ return 0; ++ } ++#endif ++ ++ /* ++ * Data checks out, process ++ */ ++ return 1; ++} ++ ++/* ++ * ubicom32hid_poll_input ++ * Poll the input from the HID device. ++ */ ++static void ubicom32hid_poll_input(struct input_polled_dev *dev) ++{ ++ struct ubicom32hid_data *ud = (struct ubicom32hid_data *)dev->private; ++ const struct ubicom32hid_platform_data *pdata = ud->pdata; ++ struct ubicom32hid_input_data data; ++ struct input_dev *id = dev->input; ++ int i; ++ int sync_needed = 0; ++ uint8_t cmd; ++ int ret; ++ ++ /* ++ * Flush the queue ++ */ ++ cmd = UBICOM32HID_CMD_FLUSH; ++ ret = i2c_master_send(ud->client, &cmd, 1); ++ if (ret < 0) { ++ return; ++ } ++ ++ ret = i2c_smbus_read_i2c_block_data( ++ ud->client, UBICOM32HID_CMD_GET_IR_SWITCH, 6, (void *)&data); ++ if (ret < 0) { ++ return; ++ } ++ ++ /* ++ * Verify the data to see if there is any action to be taken ++ */ ++ if (!ubicom32hid_verify_data(ud, &data)) { ++ return; ++ } ++ ++#ifdef DEBUG ++ if (ubicom32hid_debug) { ++ printk("Polled ircmd=%8x swstate=%2x swchanged=%2x\n", ++ data.ircmd, data.sw_state, data.sw_changed); ++ } ++#endif ++ ++ /* ++ * Process changed switches ++ */ ++ if (data.sw_changed) { ++ const struct ubicom32hid_button *ub = pdata->buttons; ++ for (i = 0; i < pdata->nbuttons; i++, ub++) { ++ uint8_t mask = (1 << ub->bit); ++ if (!(data.sw_changed & mask)) { ++ continue; ++ } ++ ++ sync_needed = 1; ++ input_event(id, ub->type, ub->code, ++ (data.sw_state & mask) ? 1 : 0); ++ } ++ } ++ if (sync_needed) { ++ input_sync(id); ++ } ++ ++ /* ++ * Process ir codes ++ */ ++ if (data.ircmd != 0xDEADBEEF) { ++ const struct ubicom32hid_ir *ui = pdata->ircodes; ++ for (i = 0; i < pdata->nircodes; i++, ui++) { ++ if (ui->ir_code == data.ircmd) { ++ /* ++ * Simulate a up/down event ++ */ ++ input_event(id, ui->type, ui->code, 1); ++ input_sync(id); ++ input_event(id, ui->type, ui->code, 0); ++ input_sync(id); ++ } ++ } ++ } ++} ++ ++ ++/* ++ * Backlight ops ++ */ ++static struct backlight_ops ubicom32hid_blops = { ++ .get_brightness = ubicom32hid_get_intensity, ++ .update_status = ubicom32hid_set_intensity, ++}; ++ ++/* ++ * ubicom32hid_probe ++ */ ++static int ubicom32hid_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct ubicom32hid_platform_data *pdata; ++ struct ubicom32hid_data *ud; ++ int ret; ++ int i; ++ u8 version[2]; ++ char buf[1]; ++ ++ pdata = client->dev.platform_data; ++ if (pdata == NULL) { ++ return -ENODEV; ++ } ++ ++ /* ++ * See if we even have a device available before allocating memory. ++ * ++ * Hard reset the device ++ */ ++ ret = gpio_request(pdata->gpio_reset, "ubicom32hid-reset"); ++ if (ret < 0) { ++ return ret; ++ } ++ gpio_direction_output(pdata->gpio_reset, pdata->gpio_reset_polarity); ++ udelay(100); ++ gpio_set_value(pdata->gpio_reset, !pdata->gpio_reset_polarity); ++ udelay(100); ++ ++ /* ++ * soft reset the device. It sometimes takes a while to do this. ++ */ ++ for (i = 0; i < 50; i++) { ++ buf[0] = UBICOM32HID_CMD_RESET; ++ ret = i2c_master_send(client, buf, 1); ++ if (ret > 0) { ++ break; ++ } ++ udelay(10000); ++ } ++ if (i == 50) { ++ dev_warn(&client->dev, "Unable to reset device\n"); ++ goto fail; ++ } ++ ++ ret = i2c_smbus_read_byte_data(client, UBICOM32HID_CMD_GET_DEVICE_ID); ++ if (ret != UBICOM32HID_DEVICE_ID) { ++ dev_warn(&client->dev, "Incorrect device id %02x\n", buf[0]); ++ ret = -ENODEV; ++ goto fail; ++ } ++ ++ ret = i2c_smbus_read_byte_data(client, UBICOM32HID_CMD_GET_VERSION); ++ if (ret < 0) { ++ dev_warn(&client->dev, "Unable to get version\n"); ++ goto fail; ++ } ++ version[0] = ret; ++ ++ ret = i2c_smbus_read_byte_data(client, UBICOM32HID_CMD_GET_REVISION); ++ if (ret < 0) { ++ dev_warn(&client->dev, "Unable to get revision\n"); ++ goto fail; ++ } ++ version[1] = ret; ++ ++ /* ++ * Allocate our private data ++ */ ++ ud = kzalloc(sizeof(struct ubicom32hid_data), GFP_KERNEL); ++ if (!ud) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ud->pdata = pdata; ++ ud->client = client; ++ ++ /* ++ * Register our backlight device ++ */ ++ ud->bldev = backlight_device_register(DRIVER_NAME, &client->dev, ++ ud, &ubicom32hid_blops); ++ if (IS_ERR(ud->bldev)) { ++ ret = PTR_ERR(ud->bldev); ++ goto fail2; ++ } ++ platform_set_drvdata(client, ud); ++ ++ /* ++ * Start up the backlight with the requested intensity ++ */ ++ ud->bldev->props.power = FB_BLANK_UNBLANK; ++ ud->bldev->props.max_brightness = ++ (pdata->type == UBICOM32HID_BL_TYPE_PWM) ? ++ UBICOM32HID_MAX_BRIGHTNESS_PWM : 1; ++ if (pdata->default_intensity < ud->bldev->props.max_brightness) { ++ ud->bldev->props.brightness = pdata->default_intensity; ++ } else { ++ dev_warn(&client->dev, "Default brightness out of range, " ++ "setting to max\n"); ++ ud->bldev->props.brightness = ud->bldev->props.max_brightness; ++ } ++ ++ ubicom32hid_set_intensity(ud->bldev); ++ ++ /* ++ * Check to see if we have any inputs ++ */ ++ if (!pdata->nbuttons && !pdata->nircodes) { ++ goto done; ++ } ++ ++ /* ++ * We have buttons or codes, we must register an input device ++ */ ++ ud->poll_dev = input_allocate_polled_device(); ++ if (!ud->poll_dev) { ++ ret = -ENOMEM; ++ goto fail3; ++ } ++ ++ /* ++ * Setup the polling to default to 100ms ++ */ ++ ud->poll_dev->poll = ubicom32hid_poll_input; ++ ud->poll_dev->poll_interval = ++ pdata->poll_interval ? pdata->poll_interval : 100; ++ ud->poll_dev->private = ud; ++ ++ ud->poll_dev->input->name = ++ pdata->input_name ? pdata->input_name : "Ubicom32HID"; ++ ud->poll_dev->input->phys = "ubicom32hid/input0"; ++ ud->poll_dev->input->dev.parent = &client->dev; ++ ud->poll_dev->input->id.bustype = BUS_I2C; ++ ++ /* ++ * Set the capabilities by running through the buttons and ir codes ++ */ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ const struct ubicom32hid_button *ub = &pdata->buttons[i]; ++ ++ input_set_capability(ud->poll_dev->input, ++ ub->type ? ub->type : EV_KEY, ub->code); ++ } ++ ++ for (i = 0; i < pdata->nircodes; i++) { ++ const struct ubicom32hid_ir *ui = &pdata->ircodes[i]; ++ ++ input_set_capability(ud->poll_dev->input, ++ ui->type ? ui->type : EV_KEY, ui->code); ++ } ++ ++ ret = input_register_polled_device(ud->poll_dev); ++ if (ret) { ++ goto fail3; ++ } ++ ++done: ++ printk(KERN_INFO DRIVER_NAME ": enabled, version=%02x.%02x\n", ++ version[0], version[1]); ++ ++ return 0; ++ ++fail3: ++ gpio_free(ud->pdata->gpio_reset); ++ backlight_device_unregister(ud->bldev); ++fail2: ++ kfree(ud); ++fail: ++ gpio_free(pdata->gpio_reset); ++ return ret; ++} ++ ++/* ++ * ubicom32hid_remove ++ */ ++static int ubicom32hid_remove(struct i2c_client *client) ++{ ++ struct ubicom32hid_data *ud = ++ (struct ubicom32hid_data *)platform_get_drvdata(client); ++ ++ gpio_free(ud->pdata->gpio_reset); ++ ++ backlight_device_unregister(ud->bldev); ++ ++ if (ud->poll_dev) { ++ input_unregister_polled_device(ud->poll_dev); ++ input_free_polled_device(ud->poll_dev); ++ } ++ ++ platform_set_drvdata(client, NULL); ++ ++ kfree(ud); ++ ++ return 0; ++} ++ ++static struct i2c_driver ubicom32hid_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = ubicom32hid_probe, ++ .remove = __exit_p(ubicom32hid_remove), ++ .id_table = ubicom32hid_id, ++}; ++ ++/* ++ * ubicom32hid_init ++ */ ++static int __init ubicom32hid_init(void) ++{ ++ return i2c_add_driver(&ubicom32hid_driver); ++} ++module_init(ubicom32hid_init); ++ ++/* ++ * ubicom32hid_exit ++ */ ++static void __exit ubicom32hid_exit(void) ++{ ++ i2c_del_driver(&ubicom32hid_driver); ++} ++module_exit(ubicom32hid_exit); ++ ++MODULE_AUTHOR("Pat Tjin <@ubicom.com>") ++MODULE_DESCRIPTION("Ubicom HID driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/ubicom32/mach-common/ubicom32input.c +@@ -0,0 +1,265 @@ ++/* ++ * arch/ubicom32/mach-common/ubicom32input.c ++ * Ubicom32 Input driver ++ * ++ * based on gpio-keys ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ * ++ * ++ * TODO: add groups for inputs which can be sampled together (i.e. I2C) ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/input.h> ++#include <linux/input-polldev.h> ++#include <linux/delay.h> ++#include <linux/gpio.h> ++ ++#include <asm/ubicom32input.h> ++ ++struct ubicom32input_data { ++ struct ubicom32input_platform_data *pdata; ++ ++ struct input_polled_dev *poll_dev; ++ ++ /* ++ * collection of previous states for buttons ++ */ ++ u8 prev_state[0]; ++}; ++ ++/* ++ * ubicom32input_poll ++ */ ++static void ubicom32input_poll(struct input_polled_dev *dev) ++{ ++ struct ubicom32input_data *ud = ++ (struct ubicom32input_data *)dev->private; ++ struct ubicom32input_platform_data *pdata = ud->pdata; ++ struct input_dev *id = dev->input; ++ int i; ++ int sync_needed = 0; ++ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ const struct ubicom32input_button *ub = &pdata->buttons[i]; ++ int state = 0; ++ ++ int val = gpio_get_value(ub->gpio); ++ ++ /* ++ * Check to see if the state changed from the last time we ++ * looked ++ */ ++ if (val == ud->prev_state[i]) { ++ continue; ++ } ++ ++ /* ++ * The state has changed, determine if we are "up" or "down" ++ */ ++ ud->prev_state[i] = val; ++ ++ if ((!val && ub->active_low) || (val && !ub->active_low)) { ++ state = 1; ++ } ++ ++ input_event(id, ub->type, ub->code, state); ++ sync_needed = 1; ++ } ++ ++ if (sync_needed) { ++ input_sync(id); ++ } ++} ++ ++/* ++ * ubicom32input_probe ++ */ ++static int __devinit ubicom32input_probe(struct platform_device *pdev) ++{ ++ int i; ++ struct ubicom32input_data *ud; ++ struct input_polled_dev *poll_dev; ++ struct input_dev *input_dev; ++ struct ubicom32input_platform_data *pdata; ++ int ret; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ return -EINVAL; ++ } ++ ++ ud = kzalloc(sizeof(struct ubicom32input_data) + ++ pdata->nbuttons, GFP_KERNEL); ++ if (!ud) { ++ return -ENOMEM; ++ } ++ ud->pdata = pdata; ++ ++ poll_dev = input_allocate_polled_device(); ++ if (!poll_dev) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ platform_set_drvdata(pdev, ud); ++ ++ ud->poll_dev = poll_dev; ++ poll_dev->private = ud; ++ poll_dev->poll = ubicom32input_poll; ++ ++ /* ++ * Set the poll interval requested, default to 50 msec ++ */ ++ if (pdata->poll_interval) { ++ poll_dev->poll_interval = pdata->poll_interval; ++ } else { ++ poll_dev->poll_interval = 50; ++ } ++ ++ /* ++ * Setup the input device ++ */ ++ input_dev = poll_dev->input; ++ input_dev->name = pdata->name ? pdata->name : "Ubicom32 Input"; ++ input_dev->phys = "ubicom32input/input0"; ++ input_dev->dev.parent = &pdev->dev; ++ input_dev->id.bustype = BUS_HOST; ++ ++ /* ++ * Reserve the GPIOs ++ */ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ const struct ubicom32input_button *ub = &pdata->buttons[i]; ++ ++ ret = gpio_request(ub->gpio, ++ ub->desc ? ub->desc : "ubicom32input"); ++ if (ret < 0) { ++ pr_err("ubicom32input: failed to request " ++ "GPIO %d ret=%d\n", ub->gpio, ret); ++ goto fail2; ++ } ++ ++ ret = gpio_direction_input(ub->gpio); ++ if (ret < 0) { ++ pr_err("ubicom32input: failed to set " ++ "GPIO %d to input ret=%d\n", ub->gpio, ret); ++ goto fail2; ++ } ++ ++ /* ++ * Set the previous state to the non-active stae ++ */ ++ ud->prev_state[i] = ub->active_low; ++ ++ input_set_capability(input_dev, ++ ub->type ? ub->type : EV_KEY, ub->code); ++ } ++ ++ /* ++ * Register ++ */ ++ ret = input_register_polled_device(ud->poll_dev); ++ if (ret) { ++ goto fail2; ++ } ++ ++ return 0; ++ ++fail2: ++ /* ++ * release the GPIOs we have already requested. ++ */ ++ while (--i >= 0) { ++ gpio_free(pdata->buttons[i].gpio); ++ } ++ ++fail: ++ printk(KERN_ERR "Ubicom32Input: Failed to register driver %d", ret); ++ platform_set_drvdata(pdev, NULL); ++ input_free_polled_device(poll_dev); ++ kfree(ud); ++ return ret; ++} ++ ++/* ++ * ubicom32input_remove ++ */ ++static int __devexit ubicom32input_remove(struct platform_device *dev) ++{ ++ struct ubicom32input_data *ud = ++ (struct ubicom32input_data *)platform_get_drvdata(dev); ++ int i; ++ ++ /* ++ * Free the GPIOs ++ */ ++ for (i = 0; i < ud->pdata->nbuttons; i++) { ++ gpio_free(ud->pdata->buttons[i].gpio); ++ } ++ ++ platform_set_drvdata(dev, NULL); ++ input_unregister_polled_device(ud->poll_dev); ++ input_free_polled_device(ud->poll_dev); ++ ++ kfree(ud); ++ ++ return 0; ++} ++ ++static struct platform_driver ubicom32input_driver = { ++ .driver = { ++ .name = "ubicom32input", ++ .owner = THIS_MODULE, ++ }, ++ .probe = ubicom32input_probe, ++ .remove = __devexit_p(ubicom32input_remove), ++}; ++ ++/* ++ * ubicom32input_init ++ */ ++static int __devinit ubicom32input_init(void) ++{ ++ return platform_driver_register(&ubicom32input_driver); ++} ++ ++/* ++ * ubicom32input_exit ++ */ ++static void __exit ubicom32input_exit(void) ++{ ++ platform_driver_unregister(&ubicom32input_driver); ++} ++ ++module_init(ubicom32input_init); ++module_exit(ubicom32input_exit); ++ ++MODULE_AUTHOR("Pat Tjin <pattjin@ubicom.com>"); ++MODULE_DESCRIPTION("Ubicom32 Input Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ubicom32-input"); +--- /dev/null ++++ b/arch/ubicom32/mach-common/ubicom32input_i2c.c +@@ -0,0 +1,325 @@ ++/* ++ * arch/ubicom32/mach-common/ubicom32input_i2c.c ++ * Ubicom32 Input driver for I2C ++ * Supports PCA953x and family ++ * ++ * We hog the I2C device, turning it all to input. ++ * ++ * Based on gpio-keys, pca953x ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/input.h> ++#include <linux/input-polldev.h> ++#include <linux/i2c.h> ++ ++#include <asm/ubicom32input_i2c.h> ++ ++#define UBICOM32INPUT_I2C_REG_INPUT 0 ++#define UBICOM32INPUT_I2C_REG_OUTPUT 1 ++#define UBICOM32INPUT_I2C_REG_INVERT 2 ++#define UBICOM32INPUT_I2C_REG_DIRECTION 3 ++ ++static const struct i2c_device_id ubicom32input_i2c_id[] = { ++ { "ubicom32in_pca9534", 8, }, ++ { "ubicom32in_pca9535", 16, }, ++ { "ubicom32in_pca9536", 4, }, ++ { "ubicom32in_pca9537", 4, }, ++ { "ubicom32in_pca9538", 8, }, ++ { "ubicom32in_pca9539", 16, }, ++ { "ubicom32in_pca9554", 8, }, ++ { "ubicom32in_pca9555", 16, }, ++ { "ubicom32in_pca9557", 8, }, ++ { "ubicom32in_max7310", 8, }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ubicom32input_i2c_id); ++ ++struct ubicom32input_i2c_data { ++ struct ubicom32input_i2c_platform_data *pdata; ++ ++ struct i2c_client *client; ++ ++ struct input_polled_dev *poll_dev; ++ ++ /* ++ * collection of previous states for buttons ++ */ ++ uint16_t prev_state; ++ ++ uint8_t ngpios; ++}; ++ ++/* ++ * ubicom32input_i2c_write_reg ++ * writes a register to the I2C device. ++ */ ++static int ubicom32input_i2c_write_reg(struct ubicom32input_i2c_data *ud, ++ int reg, uint16_t val) ++{ ++ int ret; ++ ++ if (ud->ngpios <= 8) { ++ ret = i2c_smbus_write_byte_data(ud->client, reg, val); ++ } else { ++ ret = i2c_smbus_write_word_data(ud->client, reg << 1, val); ++ } ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ubicom32input_i2c_read_reg ++ * reads a register from the I2C device. ++ */ ++static int ubicom32input_i2c_read_reg(struct ubicom32input_i2c_data *ud, ++ int reg, uint16_t *val) ++{ ++ int ret; ++ ++ if (ud->ngpios <= 8) { ++ ret = i2c_smbus_read_byte_data(ud->client, reg); ++ } else { ++ ret = i2c_smbus_read_word_data(ud->client, reg); ++ } ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ *val = (uint16_t)ret; ++ ++ return 0; ++} ++ ++/* ++ * ubicom32input_i2c_poll ++ */ ++static void ubicom32input_i2c_poll(struct input_polled_dev *dev) ++{ ++ struct ubicom32input_i2c_data *ud = ++ (struct ubicom32input_i2c_data *)dev->private; ++ struct ubicom32input_i2c_platform_data *pdata = ud->pdata; ++ struct input_dev *id = dev->input; ++ int i; ++ int sync_needed = 0; ++ uint16_t val; ++ uint16_t change_mask; ++ ++ /* ++ * Try to get the input status, if we fail, bail out, maybe we can do it ++ * next time. ++ */ ++ if (ubicom32input_i2c_read_reg(ud, UBICOM32INPUT_I2C_REG_INPUT, &val)) { ++ return; ++ } ++ ++ /* ++ * see if anything changed by using XOR ++ */ ++ change_mask = ud->prev_state ^ val; ++ ud->prev_state = val; ++ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ const struct ubicom32input_i2c_button *ub = &pdata->buttons[i]; ++ uint16_t mask = 1 << ub->bit; ++ int state = val & mask; ++ ++ /* ++ * Check to see if the state changed from the last time we ++ * looked ++ */ ++ if (!(change_mask & mask)) { ++ continue; ++ } ++ input_event(id, ub->type, ub->code, state); ++ sync_needed = 1; ++ } ++ ++ if (sync_needed) { ++ input_sync(id); ++ } ++} ++ ++/* ++ * ubicom32input_i2c_probe ++ */ ++static int __devinit ubicom32input_i2c_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int i; ++ struct ubicom32input_i2c_data *ud; ++ struct input_polled_dev *poll_dev; ++ struct input_dev *input_dev; ++ struct ubicom32input_i2c_platform_data *pdata; ++ int ret; ++ uint16_t invert_mask = 0; ++ ++ pdata = client->dev.platform_data; ++ if (!pdata) { ++ return -EINVAL; ++ } ++ ++ ud = kzalloc(sizeof(struct ubicom32input_i2c_data), GFP_KERNEL); ++ if (!ud) { ++ return -ENOMEM; ++ } ++ ud->pdata = pdata; ++ ud->client = client; ++ ud->ngpios = id->driver_data; ++ ++ poll_dev = input_allocate_polled_device(); ++ if (!poll_dev) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ ud->poll_dev = poll_dev; ++ poll_dev->private = ud; ++ poll_dev->poll = ubicom32input_i2c_poll; ++ ++ /* ++ * Set the poll interval requested, default to 100 msec ++ */ ++ if (pdata->poll_interval) { ++ poll_dev->poll_interval = pdata->poll_interval; ++ } else { ++ poll_dev->poll_interval = 100; ++ } ++ ++ /* ++ * Setup the input device ++ */ ++ input_dev = poll_dev->input; ++ input_dev->name = pdata->name ? pdata->name : "Ubicom32 Input I2C"; ++ input_dev->phys = "ubicom32input_i2c/input0"; ++ input_dev->dev.parent = &client->dev; ++ input_dev->id.bustype = BUS_I2C; ++ ++ /* ++ * Set the capabilities ++ */ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ const struct ubicom32input_i2c_button *ub = &pdata->buttons[i]; ++ ++ if (ub->active_low) { ++ invert_mask |= (1 << ub->bit); ++ } ++ ++ input_set_capability(input_dev, ++ ub->type ? ub->type : EV_KEY, ub->code); ++ } ++ ++ /* ++ * Setup the device (all inputs) ++ */ ++ ret = ubicom32input_i2c_write_reg(ud, UBICOM32INPUT_I2C_REG_DIRECTION, ++ 0xFFFF); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = ubicom32input_i2c_write_reg(ud, UBICOM32INPUT_I2C_REG_INVERT, ++ invert_mask); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* ++ * Register ++ */ ++ ret = input_register_polled_device(ud->poll_dev); ++ if (ret) { ++ goto fail; ++ } ++ ++ i2c_set_clientdata(client, ud); ++ ++ return 0; ++ ++fail: ++ printk(KERN_ERR "ubicom32input_i2c: Failed to register driver %d\n", ++ ret); ++ input_free_polled_device(poll_dev); ++ kfree(ud); ++ return ret; ++} ++ ++/* ++ * ubicom32input_i2c_remove ++ */ ++static int __devexit ubicom32input_i2c_remove(struct i2c_client *client) ++{ ++ struct ubicom32input_i2c_data *ud = ++ (struct ubicom32input_i2c_data *)i2c_get_clientdata(client); ++ ++ i2c_set_clientdata(client, NULL); ++ input_unregister_polled_device(ud->poll_dev); ++ input_free_polled_device(ud->poll_dev); ++ ++ kfree(ud); ++ ++ return 0; ++} ++ ++static struct i2c_driver ubicom32input_i2c_driver = { ++ .driver = { ++ .name = "ubicom32input_i2c", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __devexit_p(ubicom32input_i2c_remove), ++ .id_table = ubicom32input_i2c_id, ++ .probe = ubicom32input_i2c_probe, ++}; ++ ++/* ++ * ubicom32input_i2c_init ++ */ ++static int __devinit ubicom32input_i2c_init(void) ++{ ++ return i2c_add_driver(&ubicom32input_i2c_driver); ++} ++ ++/* ++ * ubicom32input_i2c_exit ++ */ ++static void __exit ubicom32input_i2c_exit(void) ++{ ++ i2c_del_driver(&ubicom32input_i2c_driver); ++} ++ ++module_init(ubicom32input_i2c_init); ++module_exit(ubicom32input_i2c_exit); ++ ++MODULE_AUTHOR("Pat Tjin <pattjin@ubicom.com>"); ++MODULE_DESCRIPTION("Ubicom32 Input Driver I2C"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ubicom32-input"); +--- /dev/null ++++ b/arch/ubicom32/mach-common/usb.c +@@ -0,0 +1,133 @@ ++/* ++ * arch/ubicom32/mach-common/ip5k_usb.c ++ * Ubicom32 architecture usb support. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com> ++ * Author: Kevin Hilman ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can ++ * redistribute it and/or modify it under the terms of the GNU General ++ * Public License as published by the Free Software Foundation, either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++#include <linux/usb/musb.h> ++#include <asm/devtree.h> ++#include <asm/ip5000.h> ++#include "usb_tio.h" ++ ++struct usbtionode *unode = NULL; ++ ++static struct resource usb_resources[] = { ++ [0] = { ++ .start = RJ + 0x800, ++ .end = RJ + 0x1000, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { /* general IRQ */ ++ .start = 1, /* this is a dummy value, the real irq number is passed from kernel_setup_param */ ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++ ++static struct musb_hdrc_eps_bits musb_eps[] = { ++ { "ep1_tx", 4, }, ++ { "ep1_rx", 4, }, ++ { "ep2_tx", 10, }, ++ { "ep2_rx", 10, }, ++ { "ep3_tx", 9, }, ++ { "ep3_rx", 9, }, ++ { "ep4_tx", 9, }, ++ { "ep4_rx", 9, }, ++ { "ep5_tx", 6, }, ++ { "ep5_rx", 6, }, ++}; ++ ++static struct musb_hdrc_config musb_config = { ++ .multipoint = true, ++ .dyn_fifo = false, ++ .soft_con = true, ++ .dma = false, ++ ++ .num_eps = 6, ++ .dma_channels = 0, ++ .ram_bits = 0, ++ .eps_bits = musb_eps, ++}; ++ ++static struct musb_hdrc_platform_data usb_data = { ++#ifdef CONFIG_USB_MUSB_OTG ++ .mode = MUSB_OTG, ++#else ++#ifdef CONFIG_USB_MUSB_HDRC_HCD ++ .mode = MUSB_HOST, ++#else ++#ifdef CONFIG_USB_GADGET_MUSB_HDRC ++ .mode = MUSB_PERIPHERAL, ++#endif ++#endif ++#endif ++ .clock = NULL, ++ .set_clock = NULL, ++ .config = &musb_config, ++}; ++ ++static struct platform_device musb_device = { ++ .name = "musb_hdrc", ++ .id = 0, ++ .dev = { ++ .platform_data = &usb_data, ++ .dma_mask = NULL, ++ .coherent_dma_mask = 0, ++ }, ++ .resource = usb_resources, ++ .num_resources = ARRAY_SIZE(usb_resources), ++}; ++ ++struct usbtio_node *usb_node = NULL; ++void ubi32_usb_init(void) ++{ ++ /* ++ * See if the usbtio is in the device tree. ++ */ ++ usb_node = (struct usbtio_node *)devtree_find_node("usbtio"); ++ if (!usb_node) { ++ printk(KERN_WARNING "usb init failed\n"); ++ return; ++ } ++ ++ usb_resources[1].start = usb_node->dn.recvirq; ++ if (platform_device_register(&musb_device) < 0) { ++ printk(KERN_ERR "Unable to register HS-USB (MUSB) device\n"); ++ return; ++ } ++} ++ ++void ubi32_usb_int_clr(void) ++{ ++ UBICOM32_IO_PORT(RJ)->int_clr = (1 << 3); ++} ++ +--- /dev/null ++++ b/arch/ubicom32/mach-common/usb_tio.c +@@ -0,0 +1,356 @@ ++/* ++ * arch/ubicom32/mach-common/usb_tio.c ++ * Linux side Ubicom USB TIO driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/spinlock.h> ++#include <linux/slab.h> ++#include <asm/devtree.h> ++#include "usb_tio.h" ++ ++#ifdef CONFIG_SMP ++static DEFINE_SPINLOCK(tio_lock); ++#define USB_TIO_LOCK(lock, flag) spin_lock_irqsave(lock, flag) ++#define USB_TIO_UNLOCK(lock, flag) spin_unlock_irqrestore(lock, flag) ++#define USB_TIO_LOCK_ISLOCKED(lock) spin_try_lock(lock) ++#else ++#define USB_TIO_LOCK(lock, flag) local_irq_save(flag) ++#define USB_TIO_UNLOCK(lock, flag) local_irq_restore(flag) ++#endif ++ ++spinlock_t usb_tio_lock; ++ ++/* ++ * usb_tio_set_hrt_interrupt() ++ */ ++static inline void usb_tio_set_hrt_interrupt(void) ++{ ++ ubicom32_set_interrupt(usb_node->dn.sendirq); ++} ++ ++static inline void usb_tio_wait_hrt(void) ++{ ++ while (unlikely(usb_node->pdesc)); ++} ++ ++#if defined(USB_TIO_DEBUG) ++static void usb_tio_request_verify_magic(volatile struct usb_tio_request *req) ++{ ++ BUG_ON(req->magic != USB_TIO_REQUEST_MAGIC2); ++} ++ ++static void usb_tio_request_clear_magic(volatile struct usb_tio_request *req) ++{ ++ req->magic = 0; ++} ++#endif ++ ++static void usb_tio_request_set_magic(volatile struct usb_tio_request *req) ++{ ++ req->magic = USB_TIO_REQUEST_MAGIC1; ++} ++ ++/* ++ * usb_tio_commit_request() ++ */ ++static inline void usb_tio_commit_request(volatile struct usb_tio_request *request) ++{ ++ wmb(); ++ usb_node->pdesc = request; ++ ++ /* ++ * next thing to do is alway checking if (usb_node->pdesc == NULL) ++ * to see if the request is done, so add a mb() here ++ */ ++ mb(); ++ usb_tio_set_hrt_interrupt(); ++} ++ ++/* ++ * usb_tio_read_u16() ++ * Synchronously read 16 bits. ++ */ ++u8_t usb_tio_read_u16(u32_t address, u16_t *data) ++{ ++ volatile struct usb_tio_request *tio_req = &usb_node->request; ++ unsigned long flag; ++ ++ /* ++ * Wait for any previous request to complete and then make this request. ++ */ ++ USB_TIO_LOCK(&tio_lock, flag); ++ usb_tio_wait_hrt(); ++ ++ /* ++ * Fill in the request. ++ */ ++ tio_req->address = address; ++ tio_req->cmd = USB_TIO_READ16_SYNC; ++ USB_TIO_REQUEST_SET_MAGIC(tio_req); ++ usb_tio_commit_request(tio_req); ++ ++ /* ++ * Wait for the result to show up. ++ */ ++ usb_tio_wait_hrt(); ++ USB_TIO_REQUEST_VERIFY_MAGIC(tio_req); ++ *data = (u16_t)tio_req->data; ++ USB_TIO_REQUEST_CLEAR_MAGIC(tio_req); ++ USB_TIO_UNLOCK(&tio_lock, flag); ++ return USB_TIO_OK; ++} ++ ++/* ++ * usb_tio_read_u8() ++ * Synchronously read 16 bits. ++ */ ++u8_t usb_tio_read_u8(u32_t address, u8_t *data) ++{ ++ volatile struct usb_tio_request *tio_req = &usb_node->request; ++ unsigned long flag; ++ ++ /* ++ * Wait for any previous request to complete and then make this request. ++ */ ++ USB_TIO_LOCK(&tio_lock, flag); ++ usb_tio_wait_hrt(); ++ ++ /* ++ * Fill in the request. ++ */ ++ tio_req->address = address; ++ tio_req->cmd = USB_TIO_READ8_SYNC; ++ USB_TIO_REQUEST_SET_MAGIC(tio_req); ++ ++ /* ++ * commit the request ++ */ ++ usb_tio_commit_request(tio_req); ++ ++ /* ++ * Wait for the result to show up. ++ */ ++ usb_tio_wait_hrt(); ++ USB_TIO_REQUEST_VERIFY_MAGIC(tio_req); ++ *data = (u8_t)tio_req->data; ++ USB_TIO_REQUEST_CLEAR_MAGIC(tio_req); ++ USB_TIO_UNLOCK(&tio_lock, flag); ++ return USB_TIO_OK; ++} ++ ++/* ++ * usb_tio_write_u16() ++ * Asynchronously write 16 bits. ++ */ ++u8_t usb_tio_write_u16(u32_t address, u16_t data) ++{ ++ volatile struct usb_tio_request *tio_req = &usb_node->request; ++ unsigned long flag; ++ ++ /* ++ * Wait for any previous write or pending read to complete. ++ */ ++ USB_TIO_LOCK(&tio_lock, flag); ++ usb_tio_wait_hrt(); ++ ++ tio_req->address = address; ++ tio_req->data = data; ++ tio_req->cmd = USB_TIO_WRITE16_ASYNC; ++ USB_TIO_REQUEST_SET_MAGIC(tio_req); ++ ++ /* ++ * commit the request ++ */ ++ usb_tio_commit_request(tio_req); ++ USB_TIO_UNLOCK(&tio_lock, flag); ++ return USB_TIO_OK; ++} ++ ++/* ++ * usb_tio_write_u8() ++ * Asynchronously write 8 bits. ++ */ ++u8_t usb_tio_write_u8(u32_t address, u8_t data) ++{ ++ volatile struct usb_tio_request *tio_req = &usb_node->request; ++ unsigned long flag; ++ ++ /* ++ * Wait for any previous write or pending read to complete. ++ */ ++ USB_TIO_LOCK(&tio_lock, flag); ++ usb_tio_wait_hrt(); ++ ++ tio_req->address = address; ++ tio_req->data = data; ++ tio_req->cmd = USB_TIO_WRITE8_ASYNC; ++ USB_TIO_REQUEST_SET_MAGIC(tio_req); ++ ++ /* ++ * commit the request ++ */ ++ usb_tio_commit_request(tio_req); ++ USB_TIO_UNLOCK(&tio_lock, flag); ++ return USB_TIO_OK; ++} ++ ++/* ++ * usb_tio_read_fifo() ++ * Synchronously read FIFO. ++ */ ++u8_t usb_tio_read_fifo(u32_t address, u32_t buffer, u32_t bytes) ++{ ++ volatile struct usb_tio_request *tio_req = &usb_node->request; ++ unsigned long flag; ++ ++ /* ++ * Wait for any previous request to complete and then make this request. ++ */ ++ USB_TIO_LOCK(&tio_lock, flag); ++ usb_tio_wait_hrt(); ++ ++ /* ++ * Fill in the request. ++ */ ++ tio_req->address = address; ++ tio_req->cmd = USB_TIO_READ_FIFO_SYNC; ++ tio_req->buffer = buffer; ++ tio_req->transfer_length = bytes; ++ USB_TIO_REQUEST_SET_MAGIC(tio_req); ++ ++ /* ++ * commit the request ++ */ ++ usb_tio_commit_request(tio_req); ++ ++ /* ++ * Wait for the result to show up. ++ */ ++ usb_tio_wait_hrt(); ++ USB_TIO_REQUEST_VERIFY_MAGIC(tio_req); ++ USB_TIO_REQUEST_CLEAR_MAGIC(tio_req); ++ USB_TIO_UNLOCK(&tio_lock, flag); ++ return USB_TIO_OK; ++} ++ ++/* ++ * usb_tio_write_fifo() ++ * Synchronously write 32 bits. ++ */ ++u8_t usb_tio_write_fifo(u32_t address, u32_t buffer, u32_t bytes) ++{ ++ volatile struct usb_tio_request *tio_req = &usb_node->request; ++ unsigned long flag; ++ ++ USB_TIO_LOCK(&tio_lock, flag); ++ usb_tio_wait_hrt(); ++ ++ tio_req->address = address; ++ tio_req->buffer = buffer; ++ tio_req->cmd = USB_TIO_WRITE_FIFO_SYNC; ++ tio_req->transfer_length = bytes; ++ USB_TIO_REQUEST_SET_MAGIC(tio_req); ++ /* ++ * commit the request ++ */ ++ usb_tio_commit_request(tio_req); ++ ++ /* ++ * Wait for the result to show up. ++ */ ++ usb_tio_wait_hrt(); ++ USB_TIO_REQUEST_VERIFY_MAGIC(tio_req); ++ USB_TIO_REQUEST_CLEAR_MAGIC(tio_req); ++ USB_TIO_UNLOCK(&tio_lock, flag); ++ return USB_TIO_OK; ++} ++ ++/* ++ * usb_tio_write_fifo_async() ++ * Asynchronously write 32 bits. ++ */ ++u8_t usb_tio_write_fifo_async(u32_t address, u32_t buffer, u32_t bytes) ++{ ++ volatile struct usb_tio_request *tio_req = &usb_node->request; ++ unsigned long flag; ++ ++ USB_TIO_LOCK(&tio_lock, flag); ++ usb_tio_wait_hrt(); ++ ++ tio_req->address = address; ++ ++ /* ++ * Is it necessary to make a local copy of the buffer? Any chance the URB is aborted before TIO finished the FIFO write? ++ */ ++ tio_req->buffer = buffer; ++ tio_req->cmd = USB_TIO_WRITE_FIFO_SYNC; ++ tio_req->transfer_length = bytes; ++ USB_TIO_REQUEST_SET_MAGIC(tio_req); ++ /* ++ * commit the request ++ */ ++ usb_tio_commit_request(tio_req); ++ USB_TIO_UNLOCK(&tio_lock, flag); ++ return USB_TIO_OK; ++} ++ ++/* ++ * usb_tio_read_int_status() ++ * read and clear the interrupt status registers ++ */ ++void usb_tio_read_int_status(u8_t *int_usb, u16_t *int_tx, u16_t *int_rx) ++{ ++ ++ /* ++ * clear the interrupt must be syncronized with the TIO thread to prevent the racing condiiton ++ * that TIO thread try to set it at same time ++ */ ++ asm volatile ( ++ "1: bset (%0), (%0), #0 \n\t" \ ++ " jmpne.f 1b \n\t" \ ++ : ++ : "a" (&usb_node->usb_vp_control) ++ : "memory", "cc" ++ ); ++ ++ *int_usb = usb_node->usb_vp_hw_int_usb; ++ *int_tx = cpu_to_le16(usb_node->usb_vp_hw_int_tx); ++ *int_rx = cpu_to_le16(usb_node->usb_vp_hw_int_rx); ++ ++ //printk(KERN_INFO "int read %x, %x, %x\n", *int_usb, *int_tx, *int_rx); ++ ++ /* ++ * The interrupt status register is read-clean, so clear it now ++ */ ++ usb_node->usb_vp_hw_int_usb = 0; ++ usb_node->usb_vp_hw_int_tx = 0; ++ usb_node->usb_vp_hw_int_rx = 0; ++ ++ /* ++ * release the lock bit ++ */ ++ usb_node->usb_vp_control &= 0xfffe; ++} +--- /dev/null ++++ b/arch/ubicom32/mach-common/usb_tio.h +@@ -0,0 +1,111 @@ ++/* ++ * arch/ubicom32/mach-common/usb_tio.h ++ * Definitions for usb_tio.c ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <asm/devtree.h> ++#include <asm/ip5000.h> ++ ++#ifndef _USB_TIO_H ++#define _USB_TIO_H ++ ++#undef USB_TIO_DEBUG ++ ++#define USB_TIO_REQUEST_MAGIC1 0x2307 ++#define USB_TIO_REQUEST_MAGIC2 0x0789 ++#if defined(USB_TIO_DEBUG) ++#define USB_TIO_REQUEST_VERIFY_MAGIC(req) usb_tio_request_verify_magic(req) ++#define USB_TIO_REQUEST_SET_MAGIC(req) usb_tio_request_set_magic(req) ++#define USB_TIO_REQUEST_CLEAR_MAGIC(req) usb_tio_request_clear_magic(req) ++#else ++#define USB_TIO_REQUEST_VERIFY_MAGIC(req) ++#define USB_TIO_REQUEST_SET_MAGIC(req) usb_tio_request_set_magic(req) ++#define USB_TIO_REQUEST_CLEAR_MAGIC(req) ++#endif ++ ++enum USB_TIO_status { ++ USB_TIO_OK, ++ USB_TIO_ERROR, ++ USB_TIO_ERROR_COMMIT, ++}; ++ ++enum USB_TIO_cmds { ++ USB_TIO_READ16_SYNC, ++ USB_TIO_READ8_SYNC, ++ USB_TIO_READ_FIFO_SYNC, ++ ++ USB_TIO_WRITE16_ASYNC, ++ USB_TIO_WRITE8_ASYNC, ++ USB_TIO_WRITE_FIFO_ASYNC, ++ ++ USB_TIO_WRITE16_SYNC, ++ USB_TIO_WRITE8_SYNC, ++ USB_TIO_WRITE_FIFO_SYNC, ++ ++}; ++ ++enum USB_TIO_state { ++ USB_TIO_NORMAL, ++ USB_TIO_DMA_SETUP, ++}; ++ ++struct usb_tio_request { ++ volatile u32_t address; ++ union { ++ volatile u32_t data; ++ volatile u32_t buffer; ++ }; ++ volatile u16_t cmd; ++ const volatile u16_t status; ++ volatile u32_t transfer_length; ++ volatile u32_t thread_mask; ++ volatile u16_t magic; ++}; ++ ++struct usbtio_node { ++ struct devtree_node dn; ++ volatile struct usb_tio_request * volatile pdesc; ++ struct usb_tio_request request; ++ volatile u32_t usb_vp_config; ++ volatile u32_t usb_vp_control; ++ const volatile u32_t usb_vp_status; ++ volatile u16_t usb_vp_hw_int_tx; ++ volatile u16_t usb_vp_hw_int_rx; ++ volatile u8_t usb_vp_hw_int_usb; ++ volatile u8_t usb_vp_hw_int_mask_usb; ++ volatile u16_t usb_vp_hw_int_mask_tx; ++ volatile u16_t usb_vp_hw_int_mask_rx; ++ ++}; ++ ++extern struct usbtio_node *usb_node; ++extern void ubi32_usb_init(void); ++#endif +--- /dev/null ++++ b/arch/ubicom32/mach-common/vdc_tio.c +@@ -0,0 +1,111 @@ ++/* ++ * arch/ubicom32/mach-common/vdc_tio.c ++ * Generic initialization for VDC ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/types.h> ++ ++#include <asm/devtree.h> ++#include <asm/vdc_tio.h> ++ ++/* ++ * Resources that this driver uses ++ */ ++static struct resource vdc_tio_resources[] = { ++ /* ++ * Send IRQ ++ */ ++ [0] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Receive IRQ (optional) ++ */ ++ [1] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Memory Mapped Registers ++ */ ++ [2] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++/* ++ * The platform_device structure which is passed to the driver ++ */ ++static struct platform_device vdc_tio_platform_device = { ++ .name = "ubicom32fb", ++ .id = -1, ++ .resource = vdc_tio_resources, ++ .num_resources = ARRAY_SIZE(vdc_tio_resources), ++}; ++ ++/* ++ * vdc_tio_init ++ * Checks the device tree and instantiates the driver if found ++ */ ++void __init vdc_tio_init(void) ++{ ++ /* ++ * Check the device tree for the vdc_tio ++ */ ++ struct vdc_tio_node *vdc_node = ++ (struct vdc_tio_node *)devtree_find_node("vdctio"); ++ if (!vdc_node) { ++ printk(KERN_WARNING "No vdc_tio found\n"); ++ return; ++ } ++ ++ /* ++ * Fill in the resources and platform data from devtree information ++ */ ++ vdc_tio_resources[0].start = vdc_node->dn.sendirq; ++ vdc_tio_resources[1].start = vdc_node->dn.recvirq; ++ vdc_tio_resources[2].start = (u32_t)vdc_node->regs; ++ vdc_tio_resources[2].end = (u32_t)vdc_node->regs + ++ sizeof(struct vdc_tio_vp_regs); ++ ++ /* ++ * Try to get the device registered ++ */ ++ if (platform_device_register(&vdc_tio_platform_device) < 0) { ++ printk(KERN_WARNING "VDC failed to register\n"); ++ } ++} +--- /dev/null ++++ b/arch/ubicom32/mach-ip5k/board-ip5160dev.c +@@ -0,0 +1,110 @@ ++/* ++ * arch/ubicom32/mach-ip5k/board-ip5160dev.c ++ * Platform initialization for ip5160dev board. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++ ++#include <asm/board.h> ++#include <asm/machdep.h> ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++#include <asm/ubicom32suart.h> ++#endif ++ ++/* ++ * Factory Default Button on the board at PXn ++ * TODO: This is just a placeholder and it needs to include proper header files ++ */ ++struct ubicom32fdb_platform_data { ++ int fdb_gpio; ++ bool fdb_polarity; ++}; ++ ++static struct ubicom32fdb_platform_data ip5160dev_fdb_data = { ++ .fdb_gpio = 0, ++ .fdb_polarity = true, ++}; ++ ++static struct platform_device ip5160dev_fdb_device = { ++ .name = "ubicom32fdb", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip5160dev_fdb_data, ++ }, ++}; ++ ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++static struct resource ip5160dev_ubicom32_suart_resources[] = { ++ { ++ .start = RD, ++ .end = RD, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = PORT_OTHER_INT(RD), ++ .end = PORT_OTHER_INT(RD), ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .start = 240000000, ++ .end = 240000000, ++ .flags = UBICOM32_SUART_IORESOURCE_CLOCK, ++ }, ++}; ++ ++static struct platform_device ip5160dev_ubicom32_suart_device = { ++ .name = "ubicom32suart", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(ip5160dev_ubicom32_suart_resources), ++ .resource = ip5160dev_ubicom32_suart_resources, ++}; ++#endif ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip5160dev_devices[] __initdata = { ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++ &ip5160dev_ubicom32_suart_device, ++#endif ++ &ip5160dev_fdb_device, ++}; ++ ++/* ++ * ip5160dev_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip5160dev_init(void) ++{ ++ ubi_gpio_init(); ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip5160dev_devices, ARRAY_SIZE(ip5160dev_devices)); ++ return 0; ++} ++ ++arch_initcall(ip5160dev_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip5k/board-ip5160rgw.c +@@ -0,0 +1,76 @@ ++/* ++ * arch/ubicom32/mach-ip5k/board-ip5160rgw.c ++ * Platform initialization for ip5160rgw board. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++#include <asm/board.h> ++#include <asm/machdep.h> ++ ++/* ++ * Factory Default Button on the board at PXn ++ * TODO: This is just a placeholder and it needs to include proper header files ++ */ ++struct ubicom32fdb_platform_data { ++ int fdb_gpio; ++ bool fdb_polarity; ++}; ++ ++static struct ubicom32fdb_platform_data ip5160rgw_fdb_data = { ++ .fdb_gpio = 0, ++ .fdb_polarity = true, ++}; ++ ++static struct platform_device ip5160rgw_fdb_device = { ++ .name = "ubicom32fdb", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip5160rgw_fdb_data, ++ }, ++}; ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip5160rgw_devices[] __initdata = { ++ &ip5160rgw_fdb_device, ++}; ++ ++/* ++ * ip5160rgw_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip5160rgw_init(void) ++{ ++ ubi_gpio_init(); ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip5160rgw_devices, ARRAY_SIZE(ip5160rgw_devices)); ++ return 0; ++} ++ ++arch_initcall(ip5160rgw_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip5k/board-ip5170dpf.c +@@ -0,0 +1,280 @@ ++/* ++ * arch/ubicom32/mach-ip5k/board-ip5170dpf.c ++ * Platform initialization for ip5160dpf board. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++#include <linux/leds.h> ++ ++#include <linux/i2c.h> ++#include <linux/i2c-gpio.h> ++ ++#include <linux/input.h> ++#include <asm/board.h> ++#include <asm/machdep.h> ++#include <asm/ubicom32hid.h> ++#include <asm/vdc_tio.h> ++ ++/* ++ * LEDs ++ * ++ * WLAN PD9 (Note this is shared with MISO, but we don't use it) ++ * WPS PD8 ++ * ++ * TODO: check triggers, are they generic? ++ */ ++static struct gpio_led ip5170dpf_gpio_leds[] = { ++ { ++ .name = "d31:green:WLAN1", ++ .default_trigger = "WLAN1", ++ .gpio = GPIO_RD_9, ++ .active_low = 1, ++ }, ++ { ++ .name = "d30:green:WPS", ++ .default_trigger = "WPS", ++ .gpio = GPIO_RD_8, ++ .active_low = 1, ++ }, ++}; ++ ++static struct gpio_led_platform_data ip5170dpf_gpio_led_platform_data = { ++ .num_leds = 2, ++ .leds = ip5170dpf_gpio_leds, ++}; ++ ++static struct platform_device ip5170dpf_gpio_leds_device = { ++ .name = "leds-gpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip5170dpf_gpio_led_platform_data, ++ }, ++}; ++ ++/* ++ * Backlight on the board PD0, hardware PWM ++ */ ++static const struct ubicom32hid_button ip5170dpf_ubicom32hid_buttons[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_UP, ++ .bit = 0, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_LEFT, ++ .bit = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_RIGHT, ++ .bit = 2, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_DOWN, ++ .bit = 3, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ENTER, ++ .bit = 4, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_MENU, ++ .bit = 5, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ESC, ++ .bit = 7, ++ }, ++}; ++ ++static const struct ubicom32hid_ir ip5170dpf_ubicom32hid_ircodes[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_UP, ++ .ir_code = 0xF807916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_DOWN, ++ .ir_code = 0xF20D916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_LEFT, ++ .ir_code = 0xF609916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_RIGHT, ++ .ir_code = 0xF40B916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ENTER, ++ .ir_code = 0xF50A916E ++ }, ++ { /* rotate */ ++ .type = EV_KEY, ++ .code = KEY_FN_F1, ++ .ir_code = 0xF906916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_MENU, ++ .ir_code = 0xF708916E ++ }, ++ { /* font size */ ++ .type = EV_KEY, ++ .code = KEY_FN_F2, ++ .ir_code = 0xF30C916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ESC, ++ .ir_code = 0xF10E916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_VOLUMEUP, ++ .ir_code = 0xF00F916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_VOLUMEDOWN, ++ .ir_code = 0xED12916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_MUTE, ++ .ir_code = 0xEA15916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_INFO, ++ .ir_code = 0xEF10916E ++ }, ++ { /* Like */ ++ .type = EV_KEY, ++ .code = KEY_FN_F3, ++ .ir_code = 0xEE11916E ++ }, ++ { /* Dislike */ ++ .type = EV_KEY, ++ .code = KEY_FN_F4, ++ .ir_code = 0xEB14916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_POWER, ++ .ir_code = 0xFD02916E ++ }, ++}; ++ ++static struct ubicom32hid_platform_data ip5170dpf_ubicom32hid_platform_data = { ++ .gpio_reset = GPIO_RA_4, ++ .gpio_reset_polarity = 0, ++ .type = UBICOM32HID_BL_TYPE_BINARY, ++ .invert = 0, ++ .default_intensity = 1, ++ .buttons = ip5170dpf_ubicom32hid_buttons, ++ .nbuttons = ARRAY_SIZE(ip5170dpf_ubicom32hid_buttons), ++ .ircodes = ip5170dpf_ubicom32hid_ircodes, ++ .nircodes = ARRAY_SIZE(ip5170dpf_ubicom32hid_ircodes), ++}; ++ ++/* ++ * Devices on the I2C bus ++ */ ++static struct i2c_board_info __initdata ip5170dpf_i2c_board_info[] = { ++ /* ++ * U24, ubicom32hid ++ */ ++ { ++ .type = "ubicom32hid", ++ .addr = 0x08, ++ .platform_data = &ip5170dpf_ubicom32hid_platform_data, ++ }, ++ ++ /* ++ * U14, CS4350 DAC, address 0x4B ++ */ ++}; ++ ++/* ++ * I2C bus on the board, SDA PF13, SCL PF14 ++ */ ++static struct i2c_gpio_platform_data ip5170dpf_i2c_data = { ++ .sda_pin = GPIO_RF_13, ++ .scl_pin = GPIO_RF_14, ++ .sda_is_open_drain = 0, ++ .scl_is_open_drain = 0, ++ .scl_is_output_only = 1, ++ .udelay = 5, ++}; ++ ++static struct platform_device ip5170dpf_i2c_device = { ++ .name = "i2c-gpio", ++ .id = 0, ++ .dev = { ++ .platform_data = &ip5170dpf_i2c_data, ++ }, ++}; ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip5170dpf_devices[] __initdata = { ++ &ip5170dpf_i2c_device, ++ &ip5170dpf_gpio_leds_device, ++}; ++ ++/* ++ * ip5170dpf_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip5170dpf_init(void) ++{ ++ ubi_gpio_init(); ++ ++ vdc_tio_init(); ++ ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip5170dpf_devices, ARRAY_SIZE(ip5170dpf_devices)); ++ ++ printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); ++ i2c_register_board_info(0, ip5170dpf_i2c_board_info, ARRAY_SIZE(ip5170dpf_i2c_board_info)); ++ ++ return 0; ++} ++ ++arch_initcall(ip5170dpf_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip5k/Kconfig +@@ -0,0 +1,32 @@ ++ ++config IP5170DPF ++ bool "IP5170DPF" ++ select UBICOM32_V3 ++ select I2C ++ select I2C_GPIO ++ select FB ++ select FB_UBICOM32 ++ select BACKLIGHT_LCD_SUPPORT ++ select BACKLIGHT_CLASS_DEVICE ++ select UBICOM_HID ++ select NEW_LEDS ++ select LEDS_CLASS ++ select LEDS_GPIO ++ select BRD_64MB ++ help ++ IP5170 Digital Picture Frame board, 8005-1113, IP5K-BEV-0011-13 v1.3 ++ ++config IP5160DEV ++ bool "IP5160Dev_Ver1Dot1" ++ select UBICOM32_V3 ++ select BRD_64MB ++ help ++ Ubicom StreamEngine 5000 Development Board, IP5K-BDV-0004-11 v1.1 ++ ++config IP5160EVAL ++ bool "IP5160RGWEval_Ver2Rev2" ++ select UBICOM32_V3 ++ select BRD_32MB ++ help ++ Ubicom StreamEngine 5000 RGW Evaluation Board, IP5K-RGW-0004-11 v2.2 ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip5k/Makefile +@@ -0,0 +1,32 @@ ++# ++# arch/ubicom32/mach-ip5k/Makefile ++# Makefile for boards which have an ip5k on them. ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++ ++obj-$(CONFIG_IP5170DPF) += board-ip5170dpf.o ++obj-$(CONFIG_IP5160DEV) += board-ip5160dev.o ++obj-$(CONFIG_IP5160EVAL) += board-ip5160rgw.o ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/board-ip7145dpf.c +@@ -0,0 +1,716 @@ ++/* ++ * arch/ubicom32/mach-ip7k/board-ip7145dpf.c ++ * Board file for IP7145DPF, rev 1.0, P/N 8007-0410 ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++ ++#include <linux/input.h> ++ ++#include <linux/i2c.h> ++#include <linux/i2c-gpio.h> ++#include <linux/i2c/pca953x.h> ++ ++#include <asm/board.h> ++#include <asm/machdep.h> ++#include <asm/ubicom32input.h> ++#include <asm/ubicom32input_i2c.h> ++#include <asm/ubicom32bl.h> ++#include <asm/ubicom32lcd.h> ++#include <asm/vdc_tio.h> ++ ++#include <asm/ubicom32sd.h> ++#include <asm/sd_tio.h> ++#include <asm/devtree.h> ++#include <asm/audio_tio.h> ++ ++#include <asm/ring_tio.h> ++ ++/****************************************************************************** ++ * SD/IO Port F (Slot 1) platform data ++ */ ++static struct resource ip7145dpf_portf_sd_resources[] = { ++ /* ++ * Send IRQ ++ */ ++ [0] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Receive IRQ ++ */ ++ [1] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Memory Mapped Registers ++ */ ++ [2] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct ubicom32sd_card ip7145dpf_portf_sd_cards[] = { ++ [0] = { ++ .pin_wp = IP7145DPF_IOB0, ++ .wp_polarity = 1, ++ .pin_pwr = IP7145DPF_IOB4, ++ .pin_cd = GPIO_RA_4, ++ }, ++ [1] = { ++ .pin_wp = IP7145DPF_IOB1, ++ .wp_polarity = 1, ++ .pin_pwr = IP7145DPF_IOB5, ++ .pin_cd = GPIO_RA_6, ++ }, ++}; ++ ++static struct ubicom32sd_platform_data ip7145dpf_portf_sd_platform_data = { ++ .ncards = 2, ++ .cards = ip7145dpf_portf_sd_cards, ++}; ++ ++static struct platform_device ip7145dpf_portf_sd_device = { ++ .name = "ubicom32sd", ++ .id = 0, ++ .resource = ip7145dpf_portf_sd_resources, ++ .num_resources = ARRAY_SIZE(ip7145dpf_portf_sd_resources), ++ .dev = { ++ .platform_data = &ip7145dpf_portf_sd_platform_data, ++ }, ++ ++}; ++ ++/* ++ * ip7145dpf_portf_sd_init ++ */ ++static void ip7145dpf_portf_sd_init(void) ++{ ++ /* ++ * Check the device tree for the sd_tio ++ */ ++ struct sd_tio_node *sd_node = (struct sd_tio_node *)devtree_find_node("portf_sd"); ++ if (!sd_node) { ++ printk(KERN_INFO "PortF SDTIO not found\n"); ++ return; ++ } ++ ++ /* ++ * Fill in the resources and platform data from devtree information ++ */ ++ ip7145dpf_portf_sd_resources[0].start = sd_node->dn.sendirq; ++ ip7145dpf_portf_sd_resources[1].start = sd_node->dn.recvirq; ++ ip7145dpf_portf_sd_resources[2].start = (u32_t)&(sd_node->regs); ++ ip7145dpf_portf_sd_resources[2].end = (u32_t)&(sd_node->regs) + sizeof(sd_node->regs); ++ ++ platform_device_register(&ip7145dpf_portf_sd_device); ++} ++ ++/****************************************************************************** ++ * SD/IO Port B (Slot 2) platform data ++ */ ++static struct resource ip7145dpf_portb_sd_resources[] = { ++ /* ++ * Send IRQ ++ */ ++ [0] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Receive IRQ ++ */ ++ [1] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Memory Mapped Registers ++ */ ++ [2] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct ubicom32sd_card ip7145dpf_portb_sd_cards[] = { ++ [0] = { ++ .pin_wp = IP7145DPF_IOB2, ++ .wp_polarity = 1, ++ .pin_pwr = IP7145DPF_IOB6, ++ .pin_cd = IP7145DPF_IOB3, ++ }, ++}; ++ ++static struct ubicom32sd_platform_data ip7145dpf_portb_sd_platform_data = { ++ .ncards = 1, ++ .cards = ip7145dpf_portb_sd_cards, ++}; ++ ++static struct platform_device ip7145dpf_portb_sd_device = { ++ .name = "ubicom32sd", ++ .id = 1, ++ .resource = ip7145dpf_portb_sd_resources, ++ .num_resources = ARRAY_SIZE(ip7145dpf_portb_sd_resources), ++ .dev = { ++ .platform_data = &ip7145dpf_portb_sd_platform_data, ++ }, ++ ++}; ++ ++/* ++ * ip7145dpf_portb_sd_init ++ */ ++static void ip7145dpf_portb_sd_init(void) ++{ ++ /* ++ * Check the device tree for the sd_tio ++ */ ++ struct sd_tio_node *sd_node = (struct sd_tio_node *)devtree_find_node("portb_sd"); ++ if (!sd_node) { ++ printk(KERN_INFO "PortB SDTIO not found\n"); ++ return; ++ } ++ ++ /* ++ * Fill in the resources and platform data from devtree information ++ */ ++ ip7145dpf_portb_sd_resources[0].start = sd_node->dn.sendirq; ++ ip7145dpf_portb_sd_resources[1].start = sd_node->dn.recvirq; ++ ip7145dpf_portb_sd_resources[2].start = (u32_t)&(sd_node->regs); ++ ip7145dpf_portb_sd_resources[2].end = (u32_t)&(sd_node->regs) + sizeof(sd_node->regs); ++ ++ platform_device_register(&ip7145dpf_portb_sd_device); ++} ++ ++ ++#ifdef IP7145DPF_USE_MMC_SPI ++/****************************************************************************** ++ * SPI over GPIO (MMC_SPI) ++ */ ++#include <linux/spi/spi.h> ++#include <linux/spi/mmc_spi.h> ++#include <linux/mmc/host.h> ++#include <asm/ubicom32-spi-gpio.h> ++ ++#define MMC_CS GPIO_RF_5 // PF5 D3 ++#define MMC_CD GPIO_RA_4 // PA4 CD ++#define MMC_WP IP7145DPF_IOB0 // IOB0 WP ++#define MMC_PWR IP7145DPF_IOB4 // IOB4 PWR ++ ++/* ++ * SPI bus over GPIO (for SD card) ++ */ ++static struct ubicom32_spi_gpio_platform_data ip7145dpf_spi_gpio_data = { ++ .pin_mosi = GPIO_RF_0, // PF0 CMD ++ .pin_miso = GPIO_RF_2, // PF2 D0 ++ .pin_clk = GPIO_RF_1, // PF1 CLK ++ .bus_num = 0, // We'll call this SPI bus 0 ++ .num_chipselect = 1, // only one device on this SPI bus ++}; ++ ++static struct platform_device ip7145dpf_spi_gpio_device = { ++ .name = "ubicom32-spi-gpio", ++ .id = 0, ++ .dev = { ++ .platform_data = &ip7145dpf_spi_gpio_data, ++ }, ++}; ++ ++/* ++ * ip7145dpf_mmc_spi_setpower_slot_a ++ * Set the power state for slot A ++ */ ++static void ip7145dpf_mmc_spi_setpower_slot_a(struct device *dev, unsigned int vdd) ++{ ++ struct mmc_spi_platform_data *pd = dev->platform_data; ++ ++ /* ++ * Power is inverted, we could tell the IOB to do it, but it's cleaner this way. ++ */ ++ if ((1 << vdd) & pd->ocr_mask) { ++ gpio_set_value(MMC_PWR, 0); ++ return; ++ } ++ gpio_set_value(MMC_PWR, 1); ++} ++ ++/* ++ * ip7145dpf_mmc_spi_get_cd_slot_a ++ * Get the CD bit for slot A ++ */ ++static int ip7145dpf_mmc_spi_get_cd_slot_a(struct device *dev) ++{ ++ /* ++ * Note that the sense of the GPIO is inverted ++ */ ++ return !gpio_get_value(MMC_CD); ++} ++ ++/* ++ * ip7145dpf_mmc_spi_get_ro_slot_a ++ * Get the WP bit for slot A ++ */ ++static int ip7145dpf_mmc_spi_get_ro_slot_a(struct device *dev) ++{ ++ /* ++ * Note that the sense of the GPIO is inverted, we could tell the IOB to do it, but ++ * it's clearer this way. ++ */ ++ return !gpio_get_value(MMC_WP); ++} ++ ++/* ++ * ip7145dpf_mmc_spi_exit_slot_a ++ * Free the appropriate GPIOs for slot A SD slot. ++ */ ++static void ip7145dpf_mmc_spi_exit_slot_a(struct device *dev, void *appdata) ++{ ++ gpio_free(MMC_CD); ++ gpio_free(MMC_CS); ++ gpio_free(MMC_WP); ++ gpio_free(MMC_PWR); ++ platform_device_unregister(&ip7145dpf_spi_gpio_device); ++} ++ ++/* ++ * ip7145dpf_mmc_spi_init_slot_a ++ * Allocate the appropriate GPIOs for slot A SD slot. ++ * WP is on IOB0, CD is PA4, CS is on PF5 ++ * TODO: make CD an interrupt ++ */ ++static int ip7145dpf_mmc_spi_init_slot_a(void) ++{ ++ int ret = gpio_request(MMC_CD, "mmc-a-cd"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request mmc-a-cd pin\n", __FUNCTION__); ++ return -ENOSYS; ++ } ++ gpio_direction_input(MMC_CD); ++ ++ ret = gpio_request(MMC_CS, "mmc-a-cs"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request mmc-a-cs pin\n", __FUNCTION__); ++ goto no_cs; ++ } ++ gpio_direction_output(MMC_CS, 0); ++ ++ ret = gpio_request(MMC_WP, "mmc-a-wp"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request mmc-a-wp pin\n", __FUNCTION__); ++ goto no_wp; ++ } ++ gpio_direction_input(MMC_WP); ++ ++ /* ++ * Start off with power off ++ */ ++ ret = gpio_request(MMC_PWR, "mmc-a-pwr"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request mmc-a-pwr pin\n", __FUNCTION__); ++ goto no_pwr; ++ } ++ ret = gpio_direction_output(MMC_PWR, 1); ++ ++ return 0; ++ ++no_pwr: ++ gpio_free(MMC_WP); ++ ++no_wp: ++ gpio_free(MMC_CS); ++ ++no_cs: ++ gpio_free(MMC_CD); ++ return -ENOSYS; ++} ++ ++/* ++ * MMC_SPI driver (currently bitbang) ++ */ ++static struct mmc_spi_platform_data ip7145dpf_mmc_platform_data = { ++ .ocr_mask = MMC_VDD_33_34, ++ .exit = ip7145dpf_mmc_spi_exit_slot_a, ++ .get_ro = ip7145dpf_mmc_spi_get_ro_slot_a, ++ .get_cd = ip7145dpf_mmc_spi_get_cd_slot_a, ++ ++ .setpower = ip7145dpf_mmc_spi_setpower_slot_a, ++ .powerup_msecs = 500, ++ ++ .detect_delay = 100, ++ ++ .caps = MMC_CAP_NEEDS_POLL, ++}; ++ ++static struct ubicom32_spi_gpio_controller_data ip7145dpf_mmc_controller_data = { ++ .pin_cs = MMC_CS, ++}; ++ ++static struct spi_board_info ip7145dpf_spi_board_info[] = { ++ { ++ .modalias = "mmc_spi", ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 2000000, ++ .platform_data = &ip7145dpf_mmc_platform_data, ++ .controller_data = &ip7145dpf_mmc_controller_data, ++ } ++}; ++#endif /* IP7145DPF_USE_MMC_SPI */ ++ ++/* ++ * ip7145dpf_u72_setup ++ * Called by I2C to tell us that u72 is setup. ++ * ++ * This function is called by I2C to tell us that u72 has been setup. All ++ * devices which rely on this chip being initialized (or even present) need to ++ * be initialized in this function otherwise they may get initialized too early. ++ * ++ * Currently the only device depending on u72 is the SPI ++ */ ++static int __init ip7145dpf_u72_setup(struct i2c_client *client, unsigned gpio, unsigned ngpio, void *context) ++{ ++#ifdef IP7145DPF_USE_MMC_SPI ++ if (ip7145dpf_mmc_spi_init_slot_a()) { ++ printk(KERN_ERR "%s: could not request mmc resources\n", __FUNCTION__); ++ } else { ++ printk(KERN_INFO "%s: registering SPI resources\n", __FUNCTION__); ++ spi_register_board_info(ip7145dpf_spi_board_info, ARRAY_SIZE(ip7145dpf_spi_board_info)); ++ platform_device_register(&ip7145dpf_spi_gpio_device); ++ } ++#else ++ /* ++ * Initialize the Port F/Port B SD slots ++ */ ++ ip7145dpf_portf_sd_init(); ++ ip7145dpf_portb_sd_init(); ++#endif ++ return 0; ++} ++ ++/****************************************************************************** ++ * LCD VGH on the board at PE6 ++ */ ++static struct ubicom32lcd_platform_data ip7145dpf_lcd_data = { ++ .vgh_gpio = GPIO_RE_6, ++ .vgh_polarity = true, ++}; ++ ++static struct platform_device ip7145dpf_lcd_device = { ++ .name = "ubicom32lcd", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip7145dpf_lcd_data, ++ }, ++}; ++ ++/****************************************************************************** ++ * Backlight on the board PD0, hardware PWM ++ */ ++static struct ubicom32bl_platform_data ip7145dpf_backlight_data = { ++ .type = UBICOM32BL_TYPE_PWM, ++ .pwm_channel = 2, ++ .pwm_prescale = 15, ++ .pwm_period = 60, ++ .default_intensity = 0x80, ++}; ++ ++static struct platform_device ip7145dpf_backlight_device = { ++ .name = "ubicom32bl", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip7145dpf_backlight_data, ++ }, ++}; ++ ++/****************************************************************************** ++ * Ubicom32Input on I2C, U48 MAX7310, address 0x18, 8 bits ++ */ ++static struct ubicom32input_i2c_button ip7145dpf_ubicom32input_i2c_u48_buttons[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_UP, ++ .bit = 0, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_LEFT, ++ .bit = 1, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_RIGHT, ++ .bit = 2, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_DOWN, ++ .bit = 3, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ENTER, ++ .bit = 4, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_MENU, ++ .bit = 5, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ESC, ++ .bit = 6, ++ .active_low = 1, ++ }, ++}; ++ ++static struct ubicom32input_i2c_platform_data ip7145dpf_ubicom32input_i2c_u48_platform_data = { ++ .buttons = ip7145dpf_ubicom32input_i2c_u48_buttons, ++ .nbuttons = ARRAY_SIZE(ip7145dpf_ubicom32input_i2c_u48_buttons), ++ .name = "Ubicom32 Input I2C U48", ++}; ++ ++/****************************************************************************** ++ * Additional GPIO chips ++ */ ++static struct pca953x_platform_data ip7145dpf_gpio_u72_platform_data = { ++ .gpio_base = IP7145DPF_U72_BASE, ++ .setup = ip7145dpf_u72_setup, ++}; ++ ++/****************************************************************************** ++ * Devices on the I2C bus ++ */ ++static struct i2c_board_info __initdata ip7145dpf_i2c_board_info[] = { ++ /* ++ * U51, S35390A RTC, address 0x30 ++ */ ++ { ++ .type = "s35390a", ++ .addr = 0x30, ++ }, ++ ++ /* ++ * U48, MAX7310 IO expander, 8 bits, address 0x18 ++ */ ++ { ++ .type = "ubicom32in_max7310", ++ .addr = 0x18, ++ .platform_data = &ip7145dpf_ubicom32input_i2c_u48_platform_data, ++ }, ++ ++ /* ++ * U72, MAX7310 IOB expander, 8 bits, address 0x19 ++ */ ++ { ++ .type = "max7310", ++ .addr = 0x19, ++ .platform_data = &ip7145dpf_gpio_u72_platform_data, ++ }, ++}; ++ ++/* ++ * I2C bus on the board, SDA PE1, SCL PE2 ++ */ ++static struct i2c_gpio_platform_data ip7145dpf_i2c_data = { ++ .sda_pin = GPIO_RE_1, ++ .scl_pin = GPIO_RE_2, ++ .sda_is_open_drain = 0, ++ .scl_is_open_drain = 0, ++}; ++ ++static struct platform_device ip7145dpf_i2c_device = { ++ .name = "i2c-gpio", ++ .id = 0, ++ .dev = { ++ .platform_data = &ip7145dpf_i2c_data, ++ }, ++}; ++ ++/****************************************************************************** ++ * Use ubicom32input driver to monitor the various pushbuttons on this board. ++ * ++ * WPS PF12 ++ * FACT_DEFAULT PF13 ++ * POWER PE4 ++ * ++ * Not sutable for the keypad buttons since those run on I2C GPIO. The polling ++ * of ubicom32input would seem to be excessive for this. ++ * ++ * TODO: pick some ubicom understood EV_xxx define for WPS and Fact Default ++ */ ++static struct ubicom32input_button ip7145dpf_ubicom32input_buttons[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_FN_F1, ++ .gpio = GPIO_RF_12, ++ .desc = "WPS", ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_FN_F2, ++ .gpio = GPIO_RF_13, ++ .desc = "Factory Default", ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_POWER, ++ .gpio = GPIO_RE_4, ++ .desc = "Power", ++ .active_low = 1, ++ }, ++}; ++ ++static struct ubicom32input_platform_data ip7145dpf_ubicom32input_data = { ++ .buttons = ip7145dpf_ubicom32input_buttons, ++ .nbuttons = ARRAY_SIZE(ip7145dpf_ubicom32input_buttons), ++}; ++ ++static struct platform_device ip7145dpf_ubicom32input_device = { ++ .name = "ubicom32input", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip7145dpf_ubicom32input_data, ++ }, ++}; ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip7145dpf_devices[] __initdata = { ++ &ip7145dpf_i2c_device, ++ &ip7145dpf_lcd_device, ++ &ip7145dpf_backlight_device, ++ &ip7145dpf_ubicom32input_device, ++}; ++ ++/* ++ * ip7145dpf_power_off ++ * Called to turn the power off for this board ++ */ ++static void ip7145dpf_power_off(void) ++{ ++ gpio_set_value(GPIO_RE_5, 0); ++} ++ ++/* ++ * ip7145dpf_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip7145dpf_init(void) ++{ ++ int ret; ++ struct platform_device *audio_dev; ++ ++ ubi_gpio_init(); ++ ++#ifdef CONFIG_UIO_UBICOM32RING ++ ring_tio_init("decoder_ring"); ++#endif ++ ++ /* ++ * Start up the video driver first ++ */ ++ vdc_tio_init(); ++ ++ /* ++ * Take over holding of the power from the system ++ */ ++ ret = gpio_request(GPIO_RE_5, "power_hold"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request power hold GPIO\n", __FUNCTION__); ++ } ++ gpio_direction_output(GPIO_RE_5, 1); ++ mach_power_off = ip7145dpf_power_off; ++ ++ /* ++ * USB SEL_HOST_USB line ++ */ ++ ret = gpio_request(GPIO_RF_11, "SEL_HOST_USB"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request SEL_HOST_USB GPIO\n", __FUNCTION__); ++ } ++ gpio_direction_output(GPIO_RF_11, 0); ++ ++ /* ++ * Setup audio ++ */ ++ audio_dev = audio_tio_alloc("snd-ubi32-generic", "audiotio-i2sout", 0); ++ if (audio_dev) { ++ platform_device_register(audio_dev); ++ } ++ ++ /* ++ * Register all of the devices we have on this board ++ */ ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip7145dpf_devices, ARRAY_SIZE(ip7145dpf_devices)); ++ ++ /* ++ * Register all of the devices which sit on the I2C bus ++ */ ++ printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); ++ i2c_register_board_info(0, ip7145dpf_i2c_board_info, ARRAY_SIZE(ip7145dpf_i2c_board_info)); ++ ++ /* ++ * We have to initialize the SPI after the I2C IOB gets setup. SPI is initialized in ++ * ip7145dpf_u72_setup ++ */ ++ ++ return 0; ++} ++ ++arch_initcall(ip7145dpf_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/board-ip7160bringup.c +@@ -0,0 +1,135 @@ ++/* ++ * arch/ubicom32/mach-ip7k/board-ip7160bringup.c ++ * Support for the IP7160 bringup board. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++#include <linux/leds.h> ++#include <linux/delay.h> ++#include <linux/input.h> ++ ++#include <asm/board.h> ++#include <asm/machdep.h> ++#include <asm/ubicom32input.h> ++ ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++#include <asm/ubicom32suart.h> ++#endif ++ ++/* ++ * Use ubicom32input driver to monitor the various pushbuttons on this board. ++ * ++ * WPS PD5 ++ * FACT_DEFAULT PD6 ++ * ++ * TODO: pick some ubicom understood EV_xxx define for WPS and Fact Default ++ */ ++static struct ubicom32input_button ip7160bringup_ubicom32input_buttons[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_FN_F1, ++ .gpio = GPIO_RD_5, ++ .desc = "WPS", ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_FN_F2, ++ .gpio = GPIO_RD_6, ++ .desc = "Factory Default", ++ .active_low = 1, ++ }, ++}; ++ ++static struct ubicom32input_platform_data ip7160bringup_ubicom32input_data = { ++ .buttons = ip7160bringup_ubicom32input_buttons, ++ .nbuttons = ARRAY_SIZE(ip7160bringup_ubicom32input_buttons), ++}; ++ ++static struct platform_device ip7160bringup_ubicom32input_device = { ++ .name = "ubicom32input", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip7160bringup_ubicom32input_data, ++ }, ++}; ++ ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++static struct resource ip7160bringup_ubicom32_suart_resources[] = { ++ { ++ .start = RE, ++ .end = RE, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = PORT_OTHER_INT(RE), ++ .end = PORT_OTHER_INT(RE), ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .start = 250000000, ++ .end = 250000000, ++ .flags = UBICOM32_SUART_IORESOURCE_CLOCK, ++ }, ++}; ++ ++static struct platform_device ip7160bringup_ubicom32_suart_device = { ++ .name = "ubicom32suart", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(ip7160bringup_ubicom32_suart_resources), ++ .resource = ip7160bringup_ubicom32_suart_resources, ++}; ++#endif ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip7160bringup_devices[] __initdata = { ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++ &ip7160bringup_ubicom32_suart_device, ++#endif ++ &ip7160bringup_ubicom32input_device, ++}; ++ ++/* ++ * ip7160bringup_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip7160bringup_init(void) ++{ ++ board_init(); ++ ++ ubi_gpio_init(); ++ ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip7160bringup_devices, ARRAY_SIZE(ip7160bringup_devices)); ++ ++ return 0; ++} ++ ++arch_initcall(ip7160bringup_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/board-ip7160dpf.c +@@ -0,0 +1,327 @@ ++/* ++ * arch/ubicom32/mach-ip7k/board-ip7160dpf.c ++ * Platform initialization for ip7160dpf board. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/gpio.h> ++ ++#include <linux/i2c.h> ++#include <linux/i2c-gpio.h> ++ ++#include <linux/input.h> ++ ++#include <asm/board.h> ++#include <asm/machdep.h> ++#include <asm/ubicom32hid.h> ++#include <asm/vdc_tio.h> ++#include <asm/audio_tio.h> ++ ++/* ++ * Backlight on the board PD0, hardware PWM ++ */ ++static const struct ubicom32hid_button ip7160dpf_ubicom32hid_buttons[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_UP, ++ .bit = 0, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_LEFT, ++ .bit = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_RIGHT, ++ .bit = 2, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_DOWN, ++ .bit = 3, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ENTER, ++ .bit = 4, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_MENU, ++ .bit = 5, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ESC, ++ .bit = 7, ++ }, ++}; ++ ++static const struct ubicom32hid_ir ip7160dpf_ubicom32hid_ircodes[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_UP, ++ .ir_code = 0xF807916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_DOWN, ++ .ir_code = 0xF20D916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_LEFT, ++ .ir_code = 0xF609916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_RIGHT, ++ .ir_code = 0xF40B916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ENTER, ++ .ir_code = 0xF50A916E ++ }, ++ { /* rotate */ ++ .type = EV_KEY, ++ .code = KEY_FN_F1, ++ .ir_code = 0xF906916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_MENU, ++ .ir_code = 0xF708916E ++ }, ++ { /* font size */ ++ .type = EV_KEY, ++ .code = KEY_FN_F2, ++ .ir_code = 0xF30C916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ESC, ++ .ir_code = 0xF10E916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_VOLUMEUP, ++ .ir_code = 0xF00F916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_VOLUMEDOWN, ++ .ir_code = 0xED12916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_MUTE, ++ .ir_code = 0xEA15916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_INFO, ++ .ir_code = 0xEF10916E ++ }, ++ { /* Like */ ++ .type = EV_KEY, ++ .code = KEY_FN_F3, ++ .ir_code = 0xEE11916E ++ }, ++ { /* Dislike */ ++ .type = EV_KEY, ++ .code = KEY_FN_F4, ++ .ir_code = 0xEB14916E ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_POWER, ++ .ir_code = 0xFD02916E ++ }, ++}; ++ ++static struct ubicom32hid_platform_data ip7160dpf_ubicom32hid_platform_data = { ++ .gpio_reset = GPIO_RI_5, ++ .gpio_reset_polarity = 0, ++ .type = UBICOM32HID_BL_TYPE_PWM, ++ .invert = 0, ++ .default_intensity = 128, ++ .buttons = ip7160dpf_ubicom32hid_buttons, ++ .nbuttons = ARRAY_SIZE(ip7160dpf_ubicom32hid_buttons), ++ .ircodes = ip7160dpf_ubicom32hid_ircodes, ++ .nircodes = ARRAY_SIZE(ip7160dpf_ubicom32hid_ircodes), ++}; ++ ++/* ++ * Devices on the I2C bus ++ * This board has a "bus 2" which is isolated from the main bus by U47 ++ * and pin RI0. It should be safe to always enable bus 2 by setting ++ * RI0 to low, however, it should be noted that on all existing configurations ++ * of this board, U49 and U51 are not populated. ++ */ ++static struct i2c_board_info __initdata ip7160dpf_i2c_board_info[] = { ++ /* ++ * U37, CS4350 DAC, address 0x4B, bus 2 ++ * THIS ENTRY MUST BE FIRST ++ */ ++ { ++ .type = "cs4350", ++ .addr = 0x4B, ++ } ++ ++ /* ++ * U24, ubicom32hid ++ */ ++ { ++ .type = "ubicom32hid", ++ .addr = 0x08, ++ .platform_data = &ip7160dpf_ubicom32hid_platform_data, ++ }, ++ ++ /* ++ * U49, ISL29001 Ambient Light Sensor, address 0x44, bus 2 (may not be stuffed) ++ */ ++ ++ /* ++ * U51, S35390A RTC, address 0x30, bus 2 (may not be stuffed) ++ */ ++#ifdef CONFIG_RTC_DRV_S35390A ++ { ++ .type = "s35390a", ++ .addr = 0x30, ++ }, ++#endif ++}; ++ ++/* ++ * I2C bus on the board, SDA PI1, SCL PI2 ++ */ ++static struct i2c_gpio_platform_data ip7160dpf_i2c_data = { ++ .sda_pin = GPIO_RI_1, ++ .scl_pin = GPIO_RI_2, ++ .sda_is_open_drain = 0, ++ .scl_is_open_drain = 0, ++ .scl_is_output_only = 1, ++ .udelay = 6, ++}; ++ ++static struct platform_device ip7160dpf_i2c_device = { ++ .name = "i2c-gpio", ++ .id = 0, ++ .dev = { ++ .platform_data = &ip7160dpf_i2c_data, ++ }, ++}; ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip7160dpf_devices[] __initdata = { ++ &ip7160dpf_i2c_device, ++}; ++ ++/* ++ * ip7160dpf_power_off ++ * Called to turn the power off for this board ++ */ ++static void ip7160dpf_power_off(void) ++{ ++ gpio_set_value(GPIO_RF_14, 0); ++} ++ ++/* ++ * ip7160dpf_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip7160dpf_init(void) ++{ ++ int ret; ++ struct platform_device *audio_dev; ++ ++ ubi_gpio_init(); ++ ++ /* ++ * Hold the POWER_HOLD line ++ */ ++ ret = gpio_request(GPIO_RF_14, "POWER_HOLD"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request POWER_HOLD GPIO\n", __FUNCTION__); ++ } ++ gpio_direction_output(GPIO_RF_14, 0); ++ mach_power_off = ip7160dpf_power_off; ++ ++ /* ++ * USB SEL_HOST_USB line ++ */ ++ ret = gpio_request(GPIO_RI_13, "SEL_HOST_USB"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request SEL_HOST_USB GPIO\n", __FUNCTION__); ++ } ++ gpio_direction_output(GPIO_RI_13, 0); ++ ++ /* ++ * USB/DAC nRESET line ++ */ ++ ret = gpio_request(GPIO_RI_3, "USB_DAC_nRESET"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request USB_DAC_nRESET GPIO\n", __FUNCTION__); ++ } ++ gpio_direction_output(GPIO_RI_3, 0); ++ udelay(1); ++ gpio_direction_output(GPIO_RI_3, 1); ++ ++ /* ++ * I2C BUS2 Disable line ++ */ ++ ret = gpio_request(GPIO_RI_0, "DISABLE_BUS2"); ++ if (ret) { ++ printk(KERN_ERR "%s: could not request DISABLE_BUS2 GPIO\n", __FUNCTION__); ++ } ++ gpio_direction_output(GPIO_RI_0, 0); ++ ++ vdc_tio_init(); ++ ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip7160dpf_devices, ARRAY_SIZE(ip7160dpf_devices)); ++ ++ /* ++ * Allocate the audio driver if we can ++ */ ++ audio_dev = audio_tio_alloc("snd-ubi32-cs4350", "audiotio-i2sout", 0); ++ if (audio_dev) { ++ ip7160dpf_i2c_board_info[0].platform_data = audio_dev; ++ } ++ ++ printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); ++ i2c_register_board_info(0, ip7160dpf_i2c_board_info, ARRAY_SIZE(ip7160dpf_i2c_board_info)); ++ ++ return 0; ++} ++ ++arch_initcall(ip7160dpf_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/board-ip7160rgw.c +@@ -0,0 +1,255 @@ ++/* ++ * arch/ubicom32/mach-ip7k/board-ip7160rgw.c ++ * Platform initialization for ip7160rgw board. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++#include <linux/leds.h> ++#include <linux/delay.h> ++#include <linux/input.h> ++#include <linux/spi/spi.h> ++ ++#include <asm/board.h> ++#include <asm/machdep.h> ++#include <asm/ubicom32input.h> ++ ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++#include <asm/ubicom32suart.h> ++#endif ++ ++#include <asm/ubicom32-spi-gpio.h> ++#include <asm/switch-bcm539x.h> ++ ++/* ++ * SPI bus over GPIO for Gigabit Ethernet Switch ++ * U58: ++ * MOSI PE0 ++ * MISO PE1 ++ * CLK PE3 ++ * CS PE2 ++ */ ++static struct ubicom32_spi_gpio_platform_data ip7160rgw_spi_gpio_data = { ++ .pin_mosi = GPIO_RE_0, ++ .pin_miso = GPIO_RE_1, ++ .pin_clk = GPIO_RE_3, ++ .bus_num = 0, // We'll call this SPI bus 0 ++ .num_chipselect = 1, // only one device on this SPI bus ++ .clk_default = 1, ++}; ++ ++static struct platform_device ip7160rgw_spi_gpio_device = { ++ .name = "ubicom32-spi-gpio", ++ .id = 0, ++ .dev = { ++ .platform_data = &ip7160rgw_spi_gpio_data, ++ }, ++}; ++ ++static struct ubicom32_spi_gpio_controller_data ip7160rgw_bcm539x_controller_data = { ++ .pin_cs = GPIO_RE_2, ++}; ++ ++static struct switch_bcm539x_platform_data ip7160rgw_bcm539x_platform_data = { ++ .flags = SWITCH_BCM539X_FLAG_HW_RESET, ++ .pin_cs = GPIO_RE_2, ++ .pin_reset = GPIO_RE_4, ++ .name = "bcm539x", ++}; ++ ++static struct spi_board_info ip7160rgw_spi_board_info[] = { ++ { ++ .modalias = "bcm539x-spi", ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 2000000, ++ .platform_data = &ip7160rgw_bcm539x_platform_data, ++ .controller_data = &ip7160rgw_bcm539x_controller_data, ++ .mode = SPI_MODE_3, ++ } ++}; ++ ++/* ++ * LEDs ++ * ++ * WLAN1 PD0 (PWM capable) ++ * WLAN2 PD1 ++ * USB2.0 PD2 ++ * Status PD3 ++ * WPS PD4 ++ * ++ * TODO: check triggers, are they generic? ++ */ ++static struct gpio_led ip7160rgw_gpio_leds[] = { ++ { ++ .name = "d53:green:WLAN1", ++ .default_trigger = "WLAN1", ++ .gpio = GPIO_RD_0, ++ .active_low = 1, ++ }, ++ { ++ .name = "d54:green:WLAN2", ++ .default_trigger = "WLAN2", ++ .gpio = GPIO_RD_1, ++ .active_low = 1, ++ }, ++ { ++ .name = "d55:green:USB", ++ .default_trigger = "USB", ++ .gpio = GPIO_RD_2, ++ .active_low = 1, ++ }, ++ { ++ .name = "d56:green:Status", ++ .default_trigger = "Status", ++ .gpio = GPIO_RD_3, ++ .active_low = 1, ++ }, ++ { ++ .name = "d57:green:WPS", ++ .default_trigger = "WPS", ++ .gpio = GPIO_RD_4, ++ .active_low = 1, ++ }, ++}; ++ ++static struct gpio_led_platform_data ip7160rgw_gpio_led_platform_data = { ++ .num_leds = 5, ++ .leds = ip7160rgw_gpio_leds, ++}; ++ ++static struct platform_device ip7160rgw_gpio_leds_device = { ++ .name = "leds-gpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip7160rgw_gpio_led_platform_data, ++ }, ++}; ++ ++/* ++ * Use ubicom32input driver to monitor the various pushbuttons on this board. ++ * ++ * WPS PD5 ++ * FACT_DEFAULT PD6 ++ * ++ * TODO: pick some ubicom understood EV_xxx define for WPS and Fact Default ++ */ ++static struct ubicom32input_button ip7160rgw_ubicom32input_buttons[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_FN_F1, ++ .gpio = GPIO_RD_5, ++ .desc = "WPS", ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_FN_F2, ++ .gpio = GPIO_RD_6, ++ .desc = "Factory Default", ++ .active_low = 1, ++ }, ++}; ++ ++static struct ubicom32input_platform_data ip7160rgw_ubicom32input_data = { ++ .buttons = ip7160rgw_ubicom32input_buttons, ++ .nbuttons = ARRAY_SIZE(ip7160rgw_ubicom32input_buttons), ++}; ++ ++static struct platform_device ip7160rgw_ubicom32input_device = { ++ .name = "ubicom32input", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip7160rgw_ubicom32input_data, ++ }, ++}; ++ ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++static struct resource ip7160rgw_ubicom32_suart_resources[] = { ++ { ++ .start = RE, ++ .end = RE, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = PORT_OTHER_INT(RE), ++ .end = PORT_OTHER_INT(RE), ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .start = 250000000, ++ .end = 250000000, ++ .flags = UBICOM32_SUART_IORESOURCE_CLOCK, ++ }, ++}; ++ ++static struct platform_device ip7160rgw_ubicom32_suart_device = { ++ .name = "ubicom32suart", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(ip7160rgw_ubicom32_suart_resources), ++ .resource = ip7160rgw_ubicom32_suart_resources, ++}; ++#endif ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip7160rgw_devices[] __initdata = { ++#ifdef CONFIG_SERIAL_UBI32_SERDES ++ &ip7160rgw_ubicom32_suart_device, ++#endif ++ &ip7160rgw_ubicom32input_device, ++ &ip7160rgw_gpio_leds_device, ++ &ip7160rgw_spi_gpio_device, ++}; ++ ++/* ++ * ip7160rgw_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip7160rgw_init(void) ++{ ++ board_init(); ++ ++ /* ++ * Rev 1.2 boards have spi in a different place than 1.1/1.0 ++ */ ++ if (strcmp(board_get_revision(), "1.2") == 0) { ++ ip7160rgw_spi_gpio_data.pin_mosi = GPIO_RD_7; ++ } ++ ++ ubi_gpio_init(); ++ ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip7160rgw_devices, ARRAY_SIZE(ip7160rgw_devices)); ++ ++ printk(KERN_INFO "%s: registering SPI resources\n", __FUNCTION__); ++ spi_register_board_info(ip7160rgw_spi_board_info, ARRAY_SIZE(ip7160rgw_spi_board_info)); ++ ++ return 0; ++} ++ ++arch_initcall(ip7160rgw_init); +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/board-ip7500av.c +@@ -0,0 +1,167 @@ ++/* ++ * arch/ubicom32/mach-ip7k/board-ip7500av.c ++ * Support for IP7500 Audio Video Board + CPU module board. ++ * ++ * This file supports the IP7500 Audio Video Board: ++ * 8007-0810 Rev 1.0 ++ * with one of the following CPU module boards: ++ * 8007-0510 Rev 1.0 ++ * 8007-0510A Rev 1.0 (with ethernet) ++ * ++ * DIP Switch SW2 configuration: (*) default ++ * POS 1: on(*) = PCI enabled, off = PCI disabled ++ * POS 2: on(*) = TTYX => PA6, off = TTYX => PF12 ++ * POS 3: on(*) = TTYY => PA7, off = TTYY => PF15 ++ * POS 4: unused ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++#include <linux/i2c.h> ++#include <linux/i2c-gpio.h> ++#include <asm/board.h> ++#include <asm/machdep.h> ++#include <asm/ring_tio.h> ++#include <asm/vdc_tio.h> ++#include <asm/audio_tio.h> ++#include <asm/ubi32-cs4384.h> ++ ++/****************************************************************************** ++ * Devices on the I2C bus ++ * ++ * BEWARE of changing the order of things in this array as we depend on ++ * certain things to be in certain places. ++ */ ++static struct i2c_board_info __initdata ip7500av_i2c_board_info[] = { ++ /* ++ * U6, CS4384 DAC, address 0x19 ++ */ ++ { ++ .type = "cs4384", ++ .addr = 0x19, ++ }, ++}; ++ ++/* ++ * I2C bus on the board, SDA PD1, SCL PD2 ++ */ ++static struct i2c_gpio_platform_data ip7500av_i2c_data = { ++ .sda_pin = GPIO_RD_6, ++ .scl_pin = GPIO_RD_3, ++ .sda_is_open_drain = 0, ++ .scl_is_open_drain = 0, ++ .udelay = 50, ++}; ++ ++static struct platform_device ip7500av_i2c_device = { ++ .name = "i2c-gpio", ++ .id = 0, ++ .dev = { ++ .platform_data = &ip7500av_i2c_data, ++ }, ++}; ++ ++/* ++ * List of possible mclks we can generate. This depends on the CPU frequency. ++ */ ++static struct ubi32_cs4384_mclk_entry ip7500av_cs4384_mclk_entries[] = { ++ { ++ .rate = 12288000, ++ .div = 44, ++ }, ++ { ++ .rate = 11289600, ++ .div = 48, ++ }, ++}; ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip7500av_devices[] __initdata = { ++ &ip7500av_i2c_device, ++}; ++ ++/* ++ * ip7500av_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip7500av_init(void) ++{ ++ struct platform_device *audio_dev; ++ struct platform_device *audio_dev2; ++ struct ubi32_cs4384_platform_data *cs4384_pd; ++ ++ board_init(); ++ ++ vdc_tio_init(); ++ ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip7500av_devices, ARRAY_SIZE(ip7500av_devices)); ++ ++ /* ++ * CS4384 DAC ++ */ ++ audio_dev = audio_tio_alloc("snd-ubi32-cs4384", "audiotio-i2sout", ++ sizeof(struct ubi32_cs4384_platform_data)); ++ if (audio_dev) { ++ /* ++ * Attempt to figure out a good divisor. This will only work ++ * assuming the core frequency is compatible. ++ */ ++ int i; ++ unsigned int freq = processor_frequency(); ++ for (i = 0; i < ARRAY_SIZE(ip7500av_cs4384_mclk_entries); i++) { ++ unsigned int div; ++ unsigned int rate = ip7500av_cs4384_mclk_entries[i].rate / 1000; ++ div = ((freq / rate) + 500) / 1000; ++ ip7500av_cs4384_mclk_entries[i].div = div; ++ printk("CS4384 mclk %d rate %u000Hz div %u act %u\n", i, rate, div, freq / div); ++ } ++ ++ cs4384_pd = audio_tio_priv(audio_dev); ++ cs4384_pd->mclk_src = UBI32_CS4384_MCLK_PWM_0; ++ cs4384_pd->n_mclk = ARRAY_SIZE(ip7500av_cs4384_mclk_entries); ++ cs4384_pd->mclk_entries = ip7500av_cs4384_mclk_entries; ++ ip7500av_i2c_board_info[0].platform_data = audio_dev; ++ } ++ ++ /* ++ * SPDIF port ++ */ ++ audio_dev2 = audio_tio_alloc("snd-ubi32-generic", "audiotio-spdifout", 0); ++ if (audio_dev2) { ++ platform_device_register(audio_dev2); ++ } ++ ++ /* ++ * Register all of the devices which sit on the I2C bus ++ */ ++ printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); ++ i2c_register_board_info(0, ip7500av_i2c_board_info, ARRAY_SIZE(ip7500av_i2c_board_info)); ++ ++ ubi_gpio_init(); ++ ++ return 0; ++} ++arch_initcall(ip7500av_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/board-ip7500media.c +@@ -0,0 +1,520 @@ ++/* ++ * arch/ubicom32/mach-ip7k/board-ip7500media.c ++ * Board file for IP7500 media board. ++ * ++ * Supports the following configuration ++ * CPU Module: ++ * P/N 8007-0510 rev 1.0 ++ * DIP Switch SW2 configuration: ++ * POS 1: on = PCI enabled ++ * POS 2: off = TTYX => PF12 ++ * POS 3: off = TTYY => PF15 ++ * POS 4: unused ++ * Media Board: ++ * P/N 8007-0610 rev 1.0 ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++ ++#include <linux/input.h> ++ ++#include <linux/i2c.h> ++#include <linux/i2c-gpio.h> ++#include <linux/i2c/pca953x.h> ++ ++#include <asm/board.h> ++#include <asm/machdep.h> ++#include <asm/ubicom32input_i2c.h> ++#include <asm/ubicom32bl.h> ++#include <asm/ubicom32lcd.h> ++#include <asm/vdc_tio.h> ++ ++#include <asm/ubicom32sd.h> ++#include <asm/sd_tio.h> ++#include <asm/devtree.h> ++#include <asm/audio_tio.h> ++ ++#include <asm/ring_tio.h> ++ ++/****************************************************************************** ++ * SD/IO Port F (Slot 1) platform data ++ */ ++static struct resource ip7500media_portf_sd_resources[] = { ++ /* ++ * Send IRQ ++ */ ++ [0] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Receive IRQ ++ */ ++ [1] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Memory Mapped Registers ++ */ ++ [2] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct ubicom32sd_card ip7500media_portf_sd_cards[] = { ++ [0] = { ++ .pin_wp = IP7500MEDIA_IO16, ++ .wp_polarity = 1, ++ .pin_pwr = IP7500MEDIA_IO20, ++ .pin_cd = IP7500MEDIA_IO23, ++ }, ++ [1] = { ++ .pin_wp = IP7500MEDIA_IO17, ++ .wp_polarity = 1, ++ .pin_pwr = IP7500MEDIA_IO21, ++ .pin_cd = IP7500MEDIA_IO24, ++ }, ++}; ++ ++static struct ubicom32sd_platform_data ip7500media_portf_sd_platform_data = { ++ .ncards = 2, ++ .cards = ip7500media_portf_sd_cards, ++}; ++ ++static struct platform_device ip7500media_portf_sd_device = { ++ .name = "ubicom32sd", ++ .id = 0, ++ .resource = ip7500media_portf_sd_resources, ++ .num_resources = ARRAY_SIZE(ip7500media_portf_sd_resources), ++ .dev = { ++ .platform_data = &ip7500media_portf_sd_platform_data, ++ }, ++ ++}; ++ ++/* ++ * ip7500media_portf_sd_init ++ */ ++static void ip7500media_portf_sd_init(void) ++{ ++ /* ++ * Check the device tree for the sd_tio ++ */ ++ struct sd_tio_node *sd_node = (struct sd_tio_node *)devtree_find_node("portf_sd"); ++ if (!sd_node) { ++ printk(KERN_INFO "PortF SDTIO not found\n"); ++ return; ++ } ++ ++ /* ++ * Fill in the resources and platform data from devtree information ++ */ ++ ip7500media_portf_sd_resources[0].start = sd_node->dn.sendirq; ++ ip7500media_portf_sd_resources[1].start = sd_node->dn.recvirq; ++ ip7500media_portf_sd_resources[2].start = (u32_t)&(sd_node->regs); ++ ip7500media_portf_sd_resources[2].end = (u32_t)&(sd_node->regs) + sizeof(sd_node->regs); ++ ++ platform_device_register(&ip7500media_portf_sd_device); ++} ++ ++/****************************************************************************** ++ * SD/IO Port B (Slot 2) platform data ++ */ ++static struct resource ip7500media_portb_sd_resources[] = { ++ /* ++ * Send IRQ ++ */ ++ [0] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Receive IRQ ++ */ ++ [1] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_IRQ, ++ }, ++ ++ /* ++ * Memory Mapped Registers ++ */ ++ [2] = { ++ /* ++ * The init routine will query the devtree and fill this in ++ */ ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct ubicom32sd_card ip7500media_portb_sd_cards[] = { ++ [0] = { ++ .pin_wp = IP7500MEDIA_IO19, ++ .wp_polarity = 1, ++ .pin_pwr = IP7500MEDIA_IO22, ++ .pin_cd = IP7500MEDIA_IO18, ++ }, ++}; ++ ++static struct ubicom32sd_platform_data ip7500media_portb_sd_platform_data = { ++ .ncards = 1, ++ .cards = ip7500media_portb_sd_cards, ++}; ++ ++static struct platform_device ip7500media_portb_sd_device = { ++ .name = "ubicom32sd", ++ .id = 1, ++ .resource = ip7500media_portb_sd_resources, ++ .num_resources = ARRAY_SIZE(ip7500media_portb_sd_resources), ++ .dev = { ++ .platform_data = &ip7500media_portb_sd_platform_data, ++ }, ++ ++}; ++ ++/* ++ * ip7500media_portb_sd_init ++ */ ++static void ip7500media_portb_sd_init(void) ++{ ++ /* ++ * Check the device tree for the sd_tio ++ */ ++ struct sd_tio_node *sd_node = (struct sd_tio_node *)devtree_find_node("portb_sd"); ++ if (!sd_node) { ++ printk(KERN_INFO "PortB SDTIO not found\n"); ++ return; ++ } ++ ++ /* ++ * Fill in the resources and platform data from devtree information ++ */ ++ ip7500media_portb_sd_resources[0].start = sd_node->dn.sendirq; ++ ip7500media_portb_sd_resources[1].start = sd_node->dn.recvirq; ++ ip7500media_portb_sd_resources[2].start = (u32_t)&(sd_node->regs); ++ ip7500media_portb_sd_resources[2].end = (u32_t)&(sd_node->regs) + sizeof(sd_node->regs); ++ ++ platform_device_register(&ip7500media_portb_sd_device); ++} ++ ++/* ++ * ip7500media_u17_setup ++ * Called by I2C to tell us that u17 is setup. ++ * ++ * This function is called by I2C to tell us that u17 has been setup. All ++ * devices which rely on this chip being initialized (or even present) need to ++ * be initialized in this function otherwise they may get initialized too early. ++ * ++ * Currently the only device depending on u17 is the SDIO ++ */ ++static int __init ip7500media_u17_setup(struct i2c_client *client, unsigned gpio, unsigned ngpio, void *context) ++{ ++ /* ++ * Initialize the Port F/Port B SD slots (only the enabled ports will init) ++ */ ++ ip7500media_portf_sd_init(); ++ ip7500media_portb_sd_init(); ++ ++ return 0; ++} ++ ++/****************************************************************************** ++ * LCD VGH on the board at PE6 ++ */ ++static struct ubicom32lcd_platform_data ip7500media_lcd_data = { ++ .vgh_gpio = GPIO_RE_7, ++ .vgh_polarity = true, ++}; ++ ++static struct platform_device ip7500media_lcd_device = { ++ .name = "ubicom32lcd", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip7500media_lcd_data, ++ }, ++}; ++ ++/****************************************************************************** ++ * Backlight on the board PD0, hardware PWM ++ */ ++static struct ubicom32bl_platform_data ip7500media_backlight_data = { ++ .type = UBICOM32BL_TYPE_PWM, ++ .pwm_channel = 2, ++ .pwm_prescale = 15, ++ .pwm_period = 60, ++ .default_intensity = 0x80, ++}; ++ ++static struct platform_device ip7500media_backlight_device = { ++ .name = "ubicom32bl", ++ .id = -1, ++ .dev = { ++ .platform_data = &ip7500media_backlight_data, ++ }, ++}; ++ ++/****************************************************************************** ++ * Ubicom32Input on I2C, U15 MAX7310, address 0x18, 8 bits ++ */ ++static struct ubicom32input_i2c_button ip7500media_ubicom32input_i2c_u15_buttons[] = { ++ { ++ .type = EV_KEY, ++ .code = KEY_LEFT, ++ .bit = 0, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_RIGHT, ++ .bit = 1, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_UP, ++ .bit = 2, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_DOWN, ++ .bit = 3, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ENTER, ++ .bit = 4, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_MENU, ++ .bit = 5, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = KEY_ESC, ++ .bit = 6, ++ .active_low = 1, ++ }, ++}; ++ ++static struct ubicom32input_i2c_platform_data ip7500media_ubicom32input_i2c_u15_platform_data = { ++ .buttons = ip7500media_ubicom32input_i2c_u15_buttons, ++ .nbuttons = ARRAY_SIZE(ip7500media_ubicom32input_i2c_u15_buttons), ++ .name = "Ubicom32 Input I2C U15", ++}; ++ ++/****************************************************************************** ++ * Additional GPIO chips ++ */ ++static struct pca953x_platform_data ip7500media_gpio_u16_platform_data = { ++ .gpio_base = IP7500MEDIA_U16_BASE, ++}; ++ ++static struct pca953x_platform_data ip7500media_gpio_u17_platform_data = { ++ .gpio_base = IP7500MEDIA_U17_BASE, ++ .setup = ip7500media_u17_setup, ++}; ++ ++static struct pca953x_platform_data ip7500media_gpio_u18_platform_data = { ++ .gpio_base = IP7500MEDIA_U18_BASE, ++}; ++ ++/****************************************************************************** ++ * Devices on the I2C bus ++ * ++ * BEWARE of changing the order of things in this array as we depend on ++ * certain things to be in certain places. ++ */ ++static struct i2c_board_info __initdata ip7500media_i2c_board_info[] = { ++ /* ++ * U6, CS4350 DAC, address 0x4B ++ */ ++ { ++ .type = "cs4350", ++ .addr = 0x4B, ++ }, ++ ++ /* ++ * U14, S35390A RTC, address 0x30 ++ */ ++ { ++ .type = "s35390a", ++ .addr = 0x30, ++ }, ++ ++ /* ++ * U15, MAX7310 IO expander, 8 bits, address 0x18 ++ * IO0: User I/O (J16-1) (Left) IO4: User I/O (J16-5) (Enter) ++ * IO1: User I/O (J16-2) (Right) IO5: User I/O (J16-6) (Menu) ++ * IO2: User I/O (J16-3) (Up) IO6: User I/O (J16-7) (Back) ++ * IO3: User I/O (J16-4) (Down) IO7: User I/O (J16-8) ++ */ ++ { ++ .type = "ubicom32in_max7310", ++ .addr = 0x18, ++ .platform_data = &ip7500media_ubicom32input_i2c_u15_platform_data, ++ }, ++ ++ /* ++ * U16, MAX7310 IO expander, 8 bits, address 0x1C ++ * IO8 : User I/O (J16-9) IO12: User I/O (J16-17) ++ * IO9 : User I/O (J16-10) IO13: User I/O (J16-18) ++ * IO10: User I/O (J16-15) IO14: User I/O (J16-19) ++ * IO11: User I/O (J16-16) IO15: User I/O (J16-20) ++ */ ++ { ++ .type = "max7310", ++ .addr = 0x1C, ++ .platform_data = &ip7500media_gpio_u16_platform_data, ++ }, ++ ++ /* ++ * U17, MAX7310 IO expander, 8 bits, address 0x1A ++ * IO16: SDIO1A_WP IO20: SD1A_PWREN ++ * IO17: SDIO1B_WP IO21: SD1B_PWREN ++ * IO18: SDIO2_CD IO22: SD2_PWREN ++ * IO19: SDIO2_WP IO23: SDIO1A_CD ++ * ++ */ ++ { ++ .type = "max7310", ++ .addr = 0x1A, ++ .platform_data = &ip7500media_gpio_u17_platform_data, ++ }, ++ ++ /* ++ * U18, MAX7310 IOB expander, 8 bits, address 0x1E ++ * IO24: SDIO1B_CD IO28: User I/O TP6 ++ * IO25: User I/O TP9 IO29: User I/O TP5 ++ * IO26: User I/O TP8 IO30: User I/O TP4 ++ * IO27: User I/O TP7 IO31: User I/O TP3 ++ */ ++ { ++ .type = "max7310", ++ .addr = 0x1E, ++ .platform_data = &ip7500media_gpio_u18_platform_data, ++ }, ++}; ++ ++/* ++ * I2C bus on the board, SDA PE4, SCL PE5 ++ */ ++static struct i2c_gpio_platform_data ip7500media_i2c_data = { ++ .sda_pin = GPIO_RE_4, ++ .scl_pin = GPIO_RE_5, ++ .sda_is_open_drain = 0, ++ .scl_is_open_drain = 0, ++ .udelay = 50, ++}; ++ ++static struct platform_device ip7500media_i2c_device = { ++ .name = "i2c-gpio", ++ .id = 0, ++ .dev = { ++ .platform_data = &ip7500media_i2c_data, ++ }, ++}; ++ ++/* ++ * List of all devices in our system ++ */ ++static struct platform_device *ip7500media_devices[] __initdata = { ++ &ip7500media_i2c_device, ++ &ip7500media_lcd_device, ++ &ip7500media_backlight_device, ++}; ++ ++/* ++ * ip7500media_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip7500media_init(void) ++{ ++ struct platform_device *audio_dev; ++ ++ board_init(); ++ ++ ubi_gpio_init(); ++ ++#ifdef CONFIG_UIO_UBICOM32RING ++ ring_tio_init("decoder_ring"); ++#endif ++ ++ /* ++ * Start up the video driver first ++ */ ++ vdc_tio_init(); ++ ++ /* ++ * Register all of the devices we have on this board ++ */ ++ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); ++ platform_add_devices(ip7500media_devices, ARRAY_SIZE(ip7500media_devices)); ++ ++ /* ++ * Allocate the audio driver if we can ++ */ ++ audio_dev = audio_tio_alloc("snd-ubi32-cs4350", "audiotio-i2sout", 0); ++ if (audio_dev) { ++ ip7500media_i2c_board_info[0].platform_data = audio_dev; ++ } ++ ++ /* ++ * Register all of the devices which sit on the I2C bus ++ */ ++ printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); ++ i2c_register_board_info(0, ip7500media_i2c_board_info, ARRAY_SIZE(ip7500media_i2c_board_info)); ++ ++ /* ++ * We have to initialize the SDIO after the I2C IOB gets setup. SDIO is initialized in ++ * ip7500media_u17_setup ++ */ ++ ++ return 0; ++} ++ ++arch_initcall(ip7500media_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/board-ip7500module.c +@@ -0,0 +1,60 @@ ++/* ++ * arch/ubicom32/mach-ip7k/board-ip7500module.c ++ * Support for IP7500 CPU module board. ++ * ++ * This file supports the IP7500 CPU module board: ++ * 8007-0510 Rev 1.0 ++ * 8007-0510A Rev 1.0 (with ethernet) ++ * ++ * DIP Switch SW2 configuration: (*) default ++ * POS 1: on(*) = PCI enabled, off = PCI disabled ++ * POS 2: on(*) = TTYX => PA6, off = TTYX => PF12 ++ * POS 3: on(*) = TTYY => PA7, off = TTYY => PF15 ++ * POS 4: unused ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++#include <asm/board.h> ++#include <asm/machdep.h> ++#include <asm/ring_tio.h> ++#include <asm/vdc_tio.h> ++ ++/* ++ * ip7500module_init ++ * Called to add the devices which we have on this board ++ */ ++static int __init ip7500module_init(void) ++{ ++ board_init(); ++ ++ ubi_gpio_init(); ++ ++ return 0; ++} ++ ++arch_initcall(ip7500module_init); ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/Kconfig +@@ -0,0 +1,132 @@ ++config IP7145DPF ++ bool "IP7145DPF" ++ select UBICOM32_V4 ++ select UBICOM_INPUT ++ select UBICOM_INPUT_I2C ++ select RTC_CLASS ++ select RTC_DRV_S35390A ++ select I2C ++ select I2C_GPIO ++ select GPIO_PCA953X ++ select FB ++ select FB_UBICOM32 ++ select BACKLIGHT_LCD_SUPPORT ++ select LCD_CLASS_DEVICE ++ select LCD_UBICOM32 ++ select BACKLIGHT_CLASS_DEVICE ++ select BACKLIGHT_UBICOM32 ++ select SND_UBI32 ++ select MMC_UBICOM32 ++ select MMC ++ select MMC_BLOCK ++ select BRD_64MB ++ help ++ IP7145 Digital Picture Frame reference design, supports: ++ 8007-0410 v1.0 ++ ++config IP7160RGW ++ bool "IP7160RGW" ++ select UBICOM32_V4 ++ select UBICOM_INPUT ++ select NEW_LEDS ++ select LEDS_CLASS ++ select LEDS_GPIO ++ select BRD_64MB ++ select SPI ++ select SPI_UBICOM32_GPIO ++ select VLAN_8021Q ++ select UBICOM_SWITCH ++ select UBICOM_SWITCH_BCM539X ++ help ++ Ubicom IP7160 RGW Eval, supports: ++ 8007-0110 v1.0 ++ 8007-0111 v1.1 ++ 8007-0112 v1.2 ++ ++config IP7160BRINGUP ++ bool "IP7160BRINGUP" ++ select UBICOM32_V4 ++ select NEW_LEDS ++ select LEDS_CLASS ++ select LEDS_GPIO ++ select BRD_64MB ++ help ++ Ubicom IP7160 Bringup, supports: ++ 8007-0010 v1.0 ++ ++config IP7160DPF ++ bool "IP7160DPF" ++ select UBICOM32_V4 ++ select I2C ++ select I2C_GPIO ++ select FB ++ select FB_UBICOM32 ++ select BACKLIGHT_LCD_SUPPORT ++ select BACKLIGHT_CLASS_DEVICE ++ select SND_UBI32 ++ select SND_UBI32_AUDIO_CS4350 ++ select UBICOM_HID ++ select BRD_64MB ++ help ++ IP7160 Digital Picture Frame board, supports: ++ 8007-0211 Rev 1.1 ++ ++config IP7500MODULE ++ bool "IP7500MODULE" ++ select UBICOM32_V4 ++ select BRD_128MB ++ help ++ Ubicom IP7500 CPU Module board, supports: ++ 8007-0510 v1.0 ++ 8007-0510A v1.0 ++ ++ Please see ip7500module.c for more details. ++ ++config IP7500AV ++ bool "IP7500AV" ++ select UBICOM32_V4 ++ select BRD_128MB ++ select I2C ++ select I2C_GPIO ++ select SND_UBI32 ++ select SND_UBI32_AUDIO_CS4384 ++ select FB ++ select FB_UBICOM32 ++ help ++ Ubicom IP7500 Audio Video board, supports: ++ 8007-0810 v1.0 ++ ++ With Ubicom IP7500 CPU Module board: ++ 8007-0510 v1.0 -or- ++ 8007-0510A v1.0 ++ ++ Please see ip7500av.c for more details. ++ ++config IP7500MEDIA ++ bool "IP7500MEDIA" ++ select UBICOM32_V4 ++ select UBICOM_INPUT_I2C ++ select RTC_CLASS ++ select RTC_DRV_S35390A ++ select I2C ++ select I2C_GPIO ++ select GPIO_PCA953X ++ select FB ++ select FB_UBICOM32 ++ select BACKLIGHT_LCD_SUPPORT ++ select LCD_CLASS_DEVICE ++ select LCD_UBICOM32 ++ select BACKLIGHT_CLASS_DEVICE ++ select BACKLIGHT_UBICOM32 ++ select SND_UBI32 ++ select SND_UBI32_AUDIO_CS4350 ++ select MMC_UBICOM32 ++ select MMC ++ select MMC_BLOCK ++ select BRD_128MB ++ help ++ IP7500 Media Board w/ IP7500 CPU Module board, supports: ++ 8007-0610 v1.0 w/ 8007-0510 v1.0 ++ ++ Please see ip7500media.c for more details. ++ +--- /dev/null ++++ b/arch/ubicom32/mach-ip7k/Makefile +@@ -0,0 +1,35 @@ ++# ++# arch/ubicom32/mach-ip7k/Makefile ++# Makefile for ip7k based boards. ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++ ++obj-$(CONFIG_IP7145DPF) += board-ip7145dpf.o ++obj-$(CONFIG_IP7160RGW) += board-ip7160rgw.o ++obj-$(CONFIG_IP7160BRINGUP) += board-ip7160bringup.o ++obj-$(CONFIG_IP7160DPF) += board-ip7160dpf.o ++obj-$(CONFIG_IP7500MODULE) += board-ip7500module.o ++obj-$(CONFIG_IP7500MEDIA) += board-ip7500media.o ++obj-$(CONFIG_IP7500AV) += board-ip7500av.o +--- /dev/null ++++ b/arch/ubicom32/Makefile +@@ -0,0 +1,103 @@ ++# ++# arch/ubicom32/Makefile ++# <TODO: Replace with short file description> ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++ ++KBUILD_DEFCONFIG := ++ ++# setup the machine name and machine dependent settings ++machine-$(CONFIG_UBICOM32_V3) := ip5k ++machine-$(CONFIG_UBICOM32_V4) := ip7k ++MACHINE := $(machine-y) ++export MACHINE ++ ++model-$(CONFIG_RAMKERNEL) := ram ++model-$(CONFIG_ROMKERNEL) := rom ++MODEL := $(model-y) ++export MODEL ++ ++CPUCLASS := $(cpuclass-y) ++ ++export CPUCLASS ++ ++# ++# We want the core kernel built using the fastcall ABI but modules need ++# to be built using the slower calling convention because they could be ++# loaded out of range for fast calls. ++# ++CFLAGS_KERNEL += -mfastcall ++CFLAGS_MODULE += -mno-fastcall ++ ++# ++# Some CFLAG additions based on specific CPU type. ++# ++cflags-$(CONFIG_UBICOM32_V3) := -march=ubicom32v3 -mno-fdpic -DIP5000 ++cflags-$(CONFIG_UBICOM32_V4) := -march=ubicom32v4 -mno-fdpic -DIP7000 ++ ++ldflags-$(CONFIG_LINKER_RELAXATION) := --relax ++LDFLAGS_vmlinux := $(ldflags-y) ++ ++GCCLIBDIR := $(dir $(shell $(CC) $(cflags-y) -print-libgcc-file-name)) ++ ++KBUILD_CFLAGS += $(cflags-y) -ffunction-sections ++KBUILD_AFLAGS += $(cflags-y) ++ ++KBUILD_CFLAGS += -D__linux__ -Dlinux ++KBUILD_CFLAGS += -DUTS_SYSNAME=\"uClinux\" ++ ++# include any machine specific directory ++ifneq ($(machine-y),) ++core-y += arch/$(ARCH)/mach-$(MACHINE)/ ++endif ++ ++head-y := arch/$(ARCH)/kernel/head.o ++ ++core-y += arch/$(ARCH)/kernel/ \ ++ arch/$(ARCH)/mm/ \ ++ arch/$(ARCH)/crypto/ \ ++ arch/$(ARCH)/mach-common/ ++ ++drivers-$(CONFIG_OPROFILE) += arch/ubicom32/oprofile/ ++ ++libs-y += arch/$(ARCH)/lib/ ++ ++archclean: ++ ++# make sure developer has selected a valid board ++ifeq ($(CONFIG_NOBOARD),y) ++# $(error have to select a valid board file $(CONFIG_NOBOARD), please run kernel config again) ++_all: config_board_error ++endif ++ ++config_board_error: ++ @echo "*************************************************" ++ @echo "You have not selected a proper board." ++ @echo "Please run menuconfig (or config) against your" ++ @echo "kernel and choose your board under Processor" ++ @echo "options" ++ @echo "*************************************************" ++ @exit 1 ++ +--- /dev/null ++++ b/arch/ubicom32/mm/fault.c +@@ -0,0 +1,81 @@ ++/* ++ * arch/ubicom32/mm/fault.c ++ * Ubicom32 architecture page fault implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, ++ * Copyright (C) 2000 Lineo, Inc. (www.lineo.com) ++ * ++ * Based on: ++ * ++ * linux/arch/m68k/mm/fault.c ++ * ++ * Copyright (C) 1995 Hamish Macdonald ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/mman.h> ++#include <linux/mm.h> ++#include <linux/kernel.h> ++#include <linux/ptrace.h> ++ ++#include <asm/system.h> ++#include <asm/pgtable.h> ++ ++extern void die_if_kernel(char *, struct pt_regs *, long); ++ ++/* ++ * This routine handles page faults. It determines the problem, and ++ * then passes it off to one of the appropriate routines. ++ * ++ * error_code: ++ * bit 0 == 0 means no page found, 1 means protection fault ++ * bit 1 == 0 means read, 1 means write ++ * ++ * If this routine detects a bad access, it returns 1, otherwise it ++ * returns 0. ++ */ ++asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, ++ unsigned long error_code) ++{ ++#ifdef DEBUG ++ printk (KERN_DEBUG "regs->sr=%#x, regs->pc=%#lx, address=%#lx, %ld\n", ++ regs->sr, regs->pc, address, error_code); ++#endif ++ ++ /* ++ * Oops. The kernel tried to access some bad page. We'll have to ++ * terminate things with extreme prejudice. ++ */ ++ if ((unsigned long) address < PAGE_SIZE) { ++ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); ++ } else ++ printk(KERN_ALERT "Unable to handle kernel access"); ++ printk(KERN_ALERT " at virtual address %08lx\n",address); ++ die_if_kernel("Oops", regs, error_code); ++ do_exit(SIGKILL); ++ ++ return 1; ++} ++ +--- /dev/null ++++ b/arch/ubicom32/mm/init.c +@@ -0,0 +1,261 @@ ++/* ++ * arch/ubicom32/mm/init.c ++ * Ubicom32 architecture virtual memory initialization. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, ++ * Kenneth Albanowski <kjahds@kjahds.com>, ++ * Copyright (C) 2000 Lineo, Inc. (www.lineo.com) ++ * ++ * Based on: ++ * ++ * linux/arch/m68k/mm/init.c ++ * ++ * Copyright (C) 1995 Hamish Macdonald ++ * ++ * JAN/1999 -- hacked to support ColdFire (gerg@snapgear.com) ++ * DEC/2000 -- linux 2.4 support <davidm@snapgear.com> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/signal.h> ++#include <linux/sched.h> ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/string.h> ++#include <linux/types.h> ++#include <linux/ptrace.h> ++#include <linux/mman.h> ++#include <linux/mm.h> ++#include <linux/swap.h> ++#include <linux/init.h> ++#include <linux/highmem.h> ++#include <linux/pagemap.h> ++#include <linux/bootmem.h> ++#include <linux/slab.h> ++ ++#include <asm/setup.h> ++#include <asm/segment.h> ++#include <asm/page.h> ++#include <asm/pgtable.h> ++#include <asm/system.h> ++#include <asm/machdep.h> ++#include <asm/ocm-alloc.h> ++ ++#undef DEBUG ++ ++extern void die_if_kernel(char *,struct pt_regs *,long); ++extern void free_initmem(void); ++ ++/* ++ * BAD_PAGE is the page that is used for page faults when linux ++ * is out-of-memory. Older versions of linux just did a ++ * do_exit(), but using this instead means there is less risk ++ * for a process dying in kernel mode, possibly leaving a inode ++ * unused etc.. ++ * ++ * BAD_PAGETABLE is the accompanying page-table: it is initialized ++ * to point to BAD_PAGE entries. ++ * ++ * ZERO_PAGE is a special page that is used for zero-initialized ++ * data and COW. ++ */ ++static unsigned long empty_bad_page_table; ++ ++static unsigned long empty_bad_page; ++ ++unsigned long empty_zero_page; ++ ++void show_mem(void) ++{ ++ unsigned long i; ++ int free = 0, total = 0, reserved = 0, shared = 0; ++ int cached = 0; ++ ++ printk(KERN_INFO "\nMem-info:\n"); ++ show_free_areas(); ++ i = max_mapnr; ++ while (i-- > 0) { ++ total++; ++ if (PageReserved(mem_map+i)) ++ reserved++; ++ else if (PageSwapCache(mem_map+i)) ++ cached++; ++ else if (!page_count(mem_map+i)) ++ free++; ++ else ++ shared += page_count(mem_map+i) - 1; ++ } ++ printk(KERN_INFO "%d pages of RAM\n",total); ++ printk(KERN_INFO "%d free pages\n",free); ++ printk(KERN_INFO "%d reserved pages\n",reserved); ++ printk(KERN_INFO "%d pages shared\n",shared); ++ printk(KERN_INFO "%d pages swap cached\n",cached); ++} ++ ++extern unsigned long memory_start; ++extern unsigned long memory_end; ++extern char __ocm_free_begin; ++extern char __ocm_free_end; ++ ++/* ++ * paging_init() continues the virtual memory environment setup which ++ * was begun by the code in arch/head.S. ++ * The parameters are pointers to where to stick the starting and ending ++ * addresses of available kernel virtual memory. ++ */ ++void __init paging_init(void) ++{ ++ /* ++ * Make sure start_mem is page aligned, otherwise bootmem and ++ * page_alloc get different views of the world. ++ */ ++#ifdef DEBUG ++ unsigned long start_mem = PAGE_ALIGN(memory_start); ++#endif ++ unsigned long end_mem = memory_end & PAGE_MASK; ++ ++#ifdef DEBUG ++ printk (KERN_DEBUG "start_mem is %#lx\nvirtual_end is %#lx\n", ++ start_mem, end_mem); ++#endif ++ ++ /* ++ * Initialize the bad page table and bad page to point ++ * to a couple of allocated pages. ++ */ ++ empty_bad_page_table = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); ++ empty_bad_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); ++ empty_zero_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); ++ memset((void *)empty_zero_page, 0, PAGE_SIZE); ++ ++ /* ++ * TODO: enable setting up for user memory management interface. ++ */ ++ ++#ifdef DEBUG ++ printk (KERN_DEBUG "before free_area_init\n"); ++ ++ printk (KERN_DEBUG "free_area_init -> start_mem is %#lx\nvirtual_end is %#lx\n", ++ start_mem, end_mem); ++#endif ++ ++ { ++ unsigned long zones_size[MAX_NR_ZONES] = {0, }; ++#ifdef CONFIG_ZONE_DMA ++ zones_size[ZONE_DMA] = OCMSIZE >> PAGE_SHIFT; ++#endif ++ zones_size[ZONE_NORMAL] = (end_mem - PAGE_OFFSET) >> PAGE_SHIFT; ++#ifdef CONFIG_HIGHMEM ++ zones_size[ZONE_HIGHMEM] = 0; ++#endif ++ free_area_init(zones_size); ++ } ++} ++ ++void __init mem_init(void) ++{ ++ int codek = 0, datak = 0, initk = 0; ++ unsigned long tmp; ++ extern char _etext, _stext, _sdata, _ebss, __init_begin, __init_end; ++ unsigned long len = CONFIG_RAMSIZE + OCMSIZE; ++ unsigned long start_mem = memory_start; /* DAVIDM - these must start at end of kernel */ ++ unsigned long end_mem = memory_end; /* DAVIDM - this must not include kernel stack at top */ ++ ++#ifdef DEBUG ++ printk(KERN_DEBUG "Mem_init: start=%lx, end=%lx\n", start_mem, end_mem); ++#endif ++ ++ end_mem &= PAGE_MASK; ++ high_memory = (void *) end_mem; ++ ++ start_mem = PAGE_ALIGN(start_mem); ++ max_mapnr = num_physpages = (((unsigned long) high_memory) - PAGE_OFFSET) >> PAGE_SHIFT; ++ ++ /* this will put all memory onto the freelists */ ++#ifdef CONFIG_ZONE_DMA ++ { ++ unsigned long ocm_free_begin = (unsigned long)&__ocm_free_begin; ++ unsigned long ocm_free_end = (unsigned long)&__ocm_free_end; ++ unsigned long zone_dma_begin = (ocm_free_begin + PAGE_SIZE - 1) & PAGE_MASK; ++ unsigned long zone_dma_end = ocm_free_end & PAGE_MASK; ++ if (zone_dma_end > zone_dma_begin) ++ free_bootmem(zone_dma_begin, zone_dma_end-zone_dma_begin); ++ } ++#endif ++ totalram_pages = free_all_bootmem(); ++ ++ codek = (&_etext - &_stext) >> 10; ++ datak = (&_ebss - &_sdata) >> 10; ++ initk = (&__init_begin - &__init_end) >> 10; ++ ++ tmp = nr_free_pages() << PAGE_SHIFT; ++ printk(KERN_INFO "Memory available: %luk/%luk RAM, (%dk kernel code, %dk data)\n", ++ tmp >> 10, ++ len >> 10, ++ codek, ++ datak ++ ); ++ ++} ++ ++#ifdef CONFIG_BLK_DEV_INITRD ++void free_initrd_mem(unsigned long start, unsigned long end) ++{ ++ int pages = 0; ++ for (; start < end; start += PAGE_SIZE) { ++ ClearPageReserved(virt_to_page(start)); ++ init_page_count(virt_to_page(start)); ++ free_page(start); ++ totalram_pages++; ++ pages++; ++ } ++ printk (KERN_NOTICE "Freeing initrd memory: %dk freed\n", pages); ++} ++#endif ++ ++void ++free_initmem() ++{ ++#ifdef CONFIG_RAMKERNEL ++ unsigned long addr; ++ extern char __init_begin, __init_end; ++ /* ++ * The following code should be cool even if these sections ++ * are not page aligned. ++ */ ++ addr = PAGE_ALIGN((unsigned long)(&__init_begin)); ++ /* next to check that the page we free is not a partial page */ ++ for (; addr + PAGE_SIZE < (unsigned long)(&__init_end); addr +=PAGE_SIZE) { ++ ClearPageReserved(virt_to_page(addr)); ++ init_page_count(virt_to_page(addr)); ++ free_page(addr); ++ totalram_pages++; ++ } ++ printk(KERN_NOTICE "Freeing unused kernel memory: %ldk freed (0x%x - 0x%x)\n", ++ (addr - PAGE_ALIGN((long) &__init_begin)) >> 10, ++ (int)(PAGE_ALIGN((unsigned long)(&__init_begin))), ++ (int)(addr - PAGE_SIZE)); ++#endif ++} ++ +--- /dev/null ++++ b/arch/ubicom32/mm/kmap.c +@@ -0,0 +1,79 @@ ++/* ++ * arch/ubicom32/mm/kmap.c ++ * Ubicom32 architecture non-mmu ioremap and friends implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2000 Lineo, <davidm@snapgear.com> ++ * Copyright (C) 2000-2002 David McCullough <davidm@snapgear.com> ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/mm.h> ++#include <linux/kernel.h> ++#include <linux/string.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/vmalloc.h> ++ ++#include <asm/setup.h> ++#include <asm/segment.h> ++#include <asm/page.h> ++#include <asm/pgalloc.h> ++#include <asm/io.h> ++#include <asm/system.h> ++ ++#undef DEBUG ++ ++/* ++ * Map some physical address range into the kernel address space. ++ */ ++void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) ++{ ++ return (void *)physaddr; ++} ++ ++/* ++ * Unmap a ioremap()ed region again. ++ */ ++void iounmap(void *addr) ++{ ++} ++ ++/* ++ * __iounmap unmaps nearly everything, so be careful ++ * it doesn't free currently pointer/page tables anymore but it ++ * wans't used anyway and might be added later. ++ */ ++void __iounmap(void *addr, unsigned long size) ++{ ++} ++ ++/* ++ * Set new cache mode for some kernel address space. ++ * The caller must push data for that range itself, if such data may already ++ * be in the cache. ++ */ ++void kernel_set_cachemode(void *addr, unsigned long size, int cmode) ++{ ++} +--- /dev/null ++++ b/arch/ubicom32/mm/Makefile +@@ -0,0 +1,32 @@ ++# ++# arch/ubicom32/mm/Makefile ++# <TODO: Replace with short file description> ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++# ++# Makefile for the linux m68knommu specific parts of the memory manager. ++# ++ ++obj-y += init.o fault.o memory.o kmap.o ocm-alloc.o +--- /dev/null ++++ b/arch/ubicom32/mm/memory.c +@@ -0,0 +1,59 @@ ++/* ++ * arch/ubicom32/mm/memory.c ++ * Ubicom32 architecture kernel_map() implementation. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, ++ * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) ++ * ++ * Based on: ++ * ++ * linux/arch/m68k/mm/memory.c ++ * ++ * Copyright (C) 1995 Hamish Macdonald ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/mm.h> ++#include <linux/kernel.h> ++#include <linux/string.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++ ++#include <asm/segment.h> ++#include <asm/page.h> ++#include <asm/pgtable.h> ++#include <asm/system.h> ++ ++/* ++ * Map some physical address range into the kernel address space. ++ * The code is copied and adapted from map_chunk(). ++ */ ++ ++unsigned long kernel_map(unsigned long paddr, unsigned long size, ++ int nocacheflag, unsigned long *memavailp ) ++{ ++ return paddr; ++} ++ +--- /dev/null ++++ b/arch/ubicom32/mm/ocm-alloc.c +@@ -0,0 +1,439 @@ ++/* ++ * arch/ubicom32/mm/ocm-alloc.c ++ * OCM allocator for Uibcom32 On-Chip memory ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright 2004-2008 Analog Devices Inc. ++ * ++ * Based on: ++ * ++ * arch/blackfin/mm/sram-alloc.c ++ * ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/miscdevice.h> ++#include <linux/ioport.h> ++#include <linux/fcntl.h> ++#include <linux/init.h> ++#include <linux/poll.h> ++#include <linux/proc_fs.h> ++#include <linux/mutex.h> ++#include <linux/rtc.h> ++#include <asm/ocm-alloc.h> ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(fmt, a...) ++#endif ++/* ++ * the data structure for OCM heap pieces ++ */ ++struct ocm_piece { ++ void *paddr; ++ int size; ++ pid_t pid; ++ struct ocm_piece *next; ++}; ++ ++/* ++ * struct ocm_heap ++ */ ++struct ocm_heap { ++ struct ocm_piece free_head; ++ struct ocm_piece used_head; ++ struct mutex lock; ++}; ++ ++static struct ocm_heap ocm_inst_heap; ++ ++/* ++ * OCM area for storing code ++ */ ++extern asmlinkage void *__ocm_inst_heap_begin; ++extern asmlinkage void *__ocm_inst_heap_end; ++#define OCM_INST_HEAP_BEGIN ((unsigned int)&__ocm_inst_heap_begin) ++#define OCM_INST_HEAP_END ((unsigned int)&__ocm_inst_heap_end) ++#define OCM_INST_HEAP_LENGTH (OCM_INST_HEAP_END - OCM_INST_HEAP_BEGIN) ++ ++static struct kmem_cache *ocm_piece_cache; ++ ++/* ++ * _ocm_heap_init() ++ */ ++static int __init _ocm_heap_init(struct ocm_heap *ocmh, ++ unsigned int start, ++ unsigned int size) ++{ ++ ocmh->free_head.next = kmem_cache_alloc(ocm_piece_cache, GFP_KERNEL); ++ ++ if (!ocmh->free_head.next) ++ return -1; ++ ++ ocmh->free_head.next->paddr = (void *)start; ++ ocmh->free_head.next->size = size; ++ ocmh->free_head.next->pid = 0; ++ ocmh->free_head.next->next = 0; ++ ++ ocmh->used_head.next = NULL; ++ ++ /* mutex initialize */ ++ mutex_init(&ocmh->lock); ++ ++ return 0; ++} ++ ++/* ++ * _ocm_alloc_init() ++ * ++ * starts the ocm heap(s) ++ */ ++static int __init _ocm_alloc_init(void) ++{ ++ if (OCM_INST_HEAP_LENGTH) { ++ ocm_piece_cache = kmem_cache_create("ocm_piece_cache", ++ sizeof(struct ocm_piece), ++ 0, SLAB_PANIC, NULL); ++ ++ if (_ocm_heap_init(&ocm_inst_heap, ++ OCM_INST_HEAP_BEGIN, ++ OCM_INST_HEAP_LENGTH) == 0) ++ printk(KERN_INFO "OCM Instruction Heap %d KB\n", ++ OCM_INST_HEAP_LENGTH >> 10); ++ else ++ printk(KERN_INFO "Failed to initialize OCM " ++ "Instruction Heap\n"); ++ ++ } else ++ printk(KERN_INFO "No space available for OCM " ++ "Instruction Heap\n"); ++ ++ return 0; ++} ++pure_initcall(_ocm_alloc_init); ++ ++/* ++ * _ocm_alloc() ++ * generic alloc a block in the ocm heap, if successful ++ * returns the pointer. ++ */ ++static void *_ocm_alloc(size_t size, pid_t pid, struct ocm_heap *ocmheap) ++{ ++ struct ocm_piece *pslot, *plast, *pavail; ++ struct ocm_piece *pfree_head = &ocmheap->free_head; ++ struct ocm_piece *pused_head = &ocmheap->used_head; ++ ++ if (size <= 0 || !pfree_head || !pused_head) ++ return NULL; ++ ++ /* Align the size */ ++ size = (size + 3) & ~3; ++ ++ pslot = pfree_head->next; ++ plast = pfree_head; ++ ++ /* ++ * search an available piece slot ++ */ ++ while (pslot != NULL && size > pslot->size) { ++ plast = pslot; ++ pslot = pslot->next; ++ } ++ ++ if (!pslot) ++ return NULL; ++ ++ if (pslot->size == size) { ++ /* ++ * Unlink this block from the list ++ */ ++ plast->next = pslot->next; ++ pavail = pslot; ++ } else { ++ /* ++ * Split this block in two. ++ */ ++ pavail = kmem_cache_alloc(ocm_piece_cache, GFP_KERNEL); ++ ++ if (!pavail) ++ return NULL; ++ ++ pavail->paddr = pslot->paddr; ++ pavail->size = size; ++ pslot->paddr += size; ++ pslot->size -= size; ++ } ++ ++ pavail->pid = pid; ++ ++ pslot = pused_head->next; ++ plast = pused_head; ++ ++ /* ++ * insert new piece into used piece list !!! ++ */ ++ while (pslot != NULL && pavail->paddr < pslot->paddr) { ++ plast = pslot; ++ pslot = pslot->next; ++ } ++ ++ pavail->next = pslot; ++ plast->next = pavail; ++ ++ DEBUGP("_ocm_alloc %d bytes at %p from in %p", ++ size, pavail->paddr, ocmheap); ++ ++ return pavail->paddr; ++} ++ ++#if 0 ++/* Allocate the largest available block. */ ++static void *_ocm_alloc_max(struct ocm_heap *ocmheap, ++ unsigned long *psize) ++{ ++ struct ocm_piece *pfree_head = &ocmheap->free_head; ++ struct ocm_piece *pslot, *pmax; ++ ++ pmax = pslot = pfree_head->next; ++ ++ /* search an available piece slot */ ++ while (pslot != NULL) { ++ if (pslot->size > pmax->size) ++ pmax = pslot; ++ pslot = pslot->next; ++ } ++ ++ if (!pmax) ++ return NULL; ++ ++ *psize = pmax->size; ++ ++ return _ocm_alloc(*psize, ocmheap); ++} ++#endif ++ ++/* ++ * _ocm_free() ++ * generic free a block in the ocm heap, if successful ++ */ ++static int _ocm_free(const void *addr, ++ struct ocm_heap *ocmheap) ++{ ++ struct ocm_piece *pslot, *plast, *pavail; ++ struct ocm_piece *pfree_head = &ocmheap->free_head; ++ struct ocm_piece *pused_head = &ocmheap->used_head; ++ ++ /* search the relevant memory slot */ ++ pslot = pused_head->next; ++ plast = pused_head; ++ ++ /* search an available piece slot */ ++ while (pslot != NULL && pslot->paddr != addr) { ++ plast = pslot; ++ pslot = pslot->next; ++ } ++ ++ if (!pslot) { ++ DEBUGP("_ocm_free %p not found in %p", addr, ocmheap); ++ return -1; ++ } ++ DEBUGP("_ocm_free %p from in %p", addr, ocmheap); ++ ++ plast->next = pslot->next; ++ pavail = pslot; ++ pavail->pid = 0; ++ ++ /* insert free pieces back to the free list */ ++ pslot = pfree_head->next; ++ plast = pfree_head; ++ ++ while (pslot != NULL && addr > pslot->paddr) { ++ plast = pslot; ++ pslot = pslot->next; ++ } ++ ++ if (plast != pfree_head && ++ plast->paddr + plast->size == pavail->paddr) { ++ plast->size += pavail->size; ++ kmem_cache_free(ocm_piece_cache, pavail); ++ } else { ++ pavail->next = plast->next; ++ plast->next = pavail; ++ plast = pavail; ++ } ++ ++ if (pslot && plast->paddr + plast->size == pslot->paddr) { ++ plast->size += pslot->size; ++ plast->next = pslot->next; ++ kmem_cache_free(ocm_piece_cache, pslot); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ocm_inst_alloc() ++ * ++ * allocates a block of size in the ocm instrction heap, if ++ * successful returns address allocated. ++ */ ++void *ocm_inst_alloc(size_t size, pid_t pid) ++{ ++ void *addr; ++ ++ if (!OCM_INST_HEAP_LENGTH) ++ return NULL; ++ ++ ++ mutex_lock(&ocm_inst_heap.lock); ++ ++ addr = _ocm_alloc(size, pid, &ocm_inst_heap); ++ ++ mutex_unlock(&ocm_inst_heap.lock); ++ ++ return addr; ++} ++EXPORT_SYMBOL(ocm_inst_alloc); ++ ++/* ++ * ocm_inst_free() ++ * free a block in the ocm instrction heap, returns 0 if successful. ++ */ ++int ocm_inst_free(const void *addr) ++{ ++ int ret; ++ ++ if (!OCM_INST_HEAP_LENGTH) ++ return -1; ++ ++ mutex_lock(&ocm_inst_heap.lock); ++ ++ ret = _ocm_free(addr, &ocm_inst_heap); ++ ++ mutex_unlock(&ocm_inst_heap.lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(ocm_inst_free); ++ ++/* ++ * ocm_free() ++ * free a block in one of the ocm heaps, returns 0 if successful. ++ */ ++int ocm_free(const void *addr) ++{ ++ if (addr >= (void *)OCM_INST_HEAP_BEGIN ++ && addr < (void *)(OCM_INST_HEAP_END)) ++ return ocm_inst_free(addr); ++ else ++ return -1; ++} ++EXPORT_SYMBOL(ocm_free); ++ ++ ++#ifdef CONFIG_PROC_FS ++/* Need to keep line of output the same. Currently, that is 46 bytes ++ * (including newline). ++ */ ++static int _ocm_proc_read(char *buf, int *len, int count, const char *desc, ++ struct ocm_heap *ocmheap) ++{ ++ struct ocm_piece *pslot; ++ struct ocm_piece *pfree_head = &ocmheap->free_head; ++ struct ocm_piece *pused_head = &ocmheap->used_head; ++ ++ /* The format is the following ++ * --- OCM 123456789012345 Size PID State \n ++ * 12345678-12345678 1234567890 12345 1234567890\n ++ */ ++ int l; ++ l = sprintf(&buf[*len], "--- OCM %-15s Size PID State \n", ++ desc); ++ ++ *len += l; ++ count -= l; ++ ++ mutex_lock(&ocm_inst_heap.lock); ++ ++ /* ++ * search the relevant memory slot ++ */ ++ pslot = pused_head->next; ++ ++ while (pslot != NULL && count > 46) { ++ l = sprintf(&buf[*len], "%p-%p %10i %5i %-10s\n", ++ pslot->paddr, pslot->paddr + pslot->size, ++ pslot->size, pslot->pid, "ALLOCATED"); ++ ++ *len += l; ++ count -= l; ++ pslot = pslot->next; ++ } ++ ++ pslot = pfree_head->next; ++ ++ while (pslot != NULL && count > 46) { ++ l = sprintf(&buf[*len], "%p-%p %10i %5i %-10s\n", ++ pslot->paddr, pslot->paddr + pslot->size, ++ pslot->size, pslot->pid, "FREE"); ++ ++ *len += l; ++ count -= l; ++ pslot = pslot->next; ++ } ++ ++ mutex_unlock(&ocm_inst_heap.lock); ++ ++ return 0; ++} ++static int ocm_proc_read(char *buf, char **start, off_t offset, int count, ++ int *eof, void *data) ++{ ++ int len = 0; ++ ++ if (_ocm_proc_read(buf, &len, count - len, "Inst Heap", ++ &ocm_inst_heap)) ++ goto not_done; ++ *eof = 1; ++ not_done: ++ return len; ++} ++ ++static int __init sram_proc_init(void) ++{ ++ struct proc_dir_entry *ptr; ++ ptr = create_proc_entry("ocm", S_IFREG | S_IRUGO, NULL); ++ if (!ptr) { ++ printk(KERN_WARNING "unable to create /proc/ocm\n"); ++ return -1; ++ } ++ ptr->owner = THIS_MODULE; ++ ptr->read_proc = ocm_proc_read; ++ return 0; ++} ++late_initcall(sram_proc_init); ++#endif +--- /dev/null ++++ b/arch/ubicom32/oprofile/ipProf.h +@@ -0,0 +1,39 @@ ++#ifndef __IP_PROF_H__ ++#define __IP_PROF_H__ ++ ++/* This number MUST match what is used in the ultra configuration! */ ++#define IPPROFILETIO_MAX_SAMPLES 600 ++ ++/* Move to .h file used in both; avoid special types */ ++struct profile_sample { ++ unsigned int pc; /* PC value */ ++ unsigned int parent; /* a5 contents, to find the caller */ ++ unsigned char cond_codes; /* for branch prediction */ ++ unsigned char thread; /* I-blocked, D-blocked, ++ 4-bit thread number */ ++ unsigned short active; /* which threads are active - ++ for accurate counting */ ++ unsigned short blocked; /* which threads are blocked due to ++ I or D cache misses */ ++ unsigned int latency; /* CPU clocks since the last message ++ dispatch in this thread ++ (thread 0 only for now) */ ++}; ++ ++ ++struct profilenode { ++ struct devtree_node dn; ++ volatile unsigned char enabled; /* Is the tio enabled to ++ take samples? */ ++ volatile unsigned char busy; /* set when the samples ++ are being read */ ++ volatile unsigned int mask; /* Threads that change the MT_EN flag */ ++ volatile unsigned short rate; /* What is the sampling rate? */ ++ volatile unsigned short head; /* sample taker puts samples here */ ++ volatile unsigned short tail; /* packet filler takes samples here */ ++ volatile unsigned short count; /* number of valid samples */ ++ volatile unsigned short total; /* Total samples */ ++ struct profile_sample samples[IPPROFILETIO_MAX_SAMPLES]; ++}; ++ ++#endif +--- /dev/null ++++ b/arch/ubicom32/oprofile/Makefile +@@ -0,0 +1,37 @@ ++# ++# arch/ubicom32/Makefile ++# Makefile for Oprofile support on Ubicom32 ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++ ++obj-$(CONFIG_OPROFILE) += oprofile.o ++ ++DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ ++ oprof.o cpu_buffer.o buffer_sync.o \ ++ event_buffer.o oprofile_files.o \ ++ oprofilefs.o oprofile_stats.o \ ++ timer_int.o ) ++ ++oprofile-y := $(DRIVER_OBJS) profile.o +--- /dev/null ++++ b/arch/ubicom32/oprofile/profile.c +@@ -0,0 +1,221 @@ ++/* ++ * arch/ubicom32/oprofile/profile.c ++ * Oprofile support for arch Ubicom32 ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) ++ * any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it will ++ * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, see ++ * <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++/** ++ * @file profile.c ++ * ++ * @remark Copyright 2002 OProfile authors ++ * @remark Read the file COPYING ++ * ++ * @author Hunyue Yau <hy@hy-research.com> ++ */ ++ ++#include <linux/oprofile.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++ ++#include <asm/devtree.h> ++#include <asm/thread.h> ++ ++/* For identifying userland vs kernel address */ ++#include <asm/stacktrace.h> ++#include "ipProf.h" ++ ++/* For communications with the backend */ ++static struct profilenode *profile_node; ++ ++/* Bitmask containing all Linux threads - as seen by the ROSR reg */ ++static unsigned long th_all_mask; ++ ++/* Lookup table to translate a hardware thread into a CPU identifier ++ * Table is indexed by the ROSR value which is assumed to be ++ * relatively small (0...15). ++ */ ++unsigned int cpu_map[THREAD_ARCHITECTURAL_MAX]; ++ ++static struct pt_regs regs; ++ ++/* ++ * For each sample returned, checked to see if they are relevant to ++ * us. This is necessary as the ubicom32 architecture has other software ++ * running outside of Linux. Only then, put the sample into the relevant ++ * cpu bins. ++ * ++ * To minimize overhead, a global mask with all possible threads of in ++ * interest to us is used as a first check. Then a second mask identifying ++ * the thread is used to obtain an identifier for that "CPU". ++ */ ++ ++/* ++ * ubicom32_build_cpu_th_mask() ++ * ++ * Build a lookup table for translation between hardware thread ++ * "ROSR" values and Linux CPU ids ++ * ++ * *** This gets executed on all CPUs at once! *** ++ */ ++static void ubicom32_build_cpu_th_mask(void *mask) ++{ ++ thread_t self = thread_get_self(); ++ unsigned long *th_m = mask; ++ ++ BUG_ON(self <= 0 || self >= THREAD_ARCHITECTURAL_MAX); ++ cpu_map[self] = smp_processor_id(); ++ ++ set_bit(self, th_m); ++} ++ ++/* ++ * profile_interrupt() ++ * ++ * Process samples returned from the profiler backend. The backend ++ * may return samples that are irrelevant to us or may even return ++ * multiple samples for the same CPU. Note that the sames may be ++ * for ANY cpu. At this time, this is unique and to support this requires ++ * Oprofile to expose an interface to accept the CPU that the same came ++ * frome. ++ */ ++static irqreturn_t profile_interrupt(int irq, void *arg) ++{ ++ int i, buf_entry; ++ int is_kernel; ++ unsigned int bit_th; ++ unsigned int th; ++ ++ if (!(profile_node->enabled) || profile_node->count < 0) { ++ printk(KERN_WARNING ++ "Unexpected interrupt, no samples or not enabled!\n"); ++ return IRQ_HANDLED; ++ } ++ ++ profile_node->busy = 1; /* Keep backend out */ ++ ++ for (i = 0; i < profile_node->count; i++) { ++ buf_entry = profile_node->tail; ++ profile_node->tail++; ++ profile_node->tail %= IPPROFILETIO_MAX_SAMPLES; ++ ++ /* Note - the "thread" ID is only the lower 4 bits */ ++ th = (0x0f & profile_node->samples[buf_entry].thread); ++ bit_th = (1 << th); ++ ++ if ((bit_th & th_all_mask) == 0) ++ continue; ++ ++ regs.pc = profile_node->samples[buf_entry].pc; ++ ++ is_kernel = ubicom32_is_kernel(regs.pc); ++ ++ oprofile_add_ext_sample_cpu(regs.pc, ®s, 0, is_kernel, ++ cpu_map[th]); ++ } ++ profile_node->count = 0; ++ profile_node->busy = 0; ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * profile_start() ++ * ++ * Notification from oprofile to start the profiler ++ */ ++static int profile_start(void) ++{ ++ if (!profile_node) ++ return -1; ++ ++ profile_node->enabled = 1; ++ ++ return 0; ++} ++ ++/* ++ * profile_stop() ++ * ++ * Notification from oprofile to stop the profiler ++ */ ++static void profile_stop(void) ++{ ++ if (profile_node) ++ profile_node->enabled = 0; ++} ++ ++/* ++ * oprofile_arch_init() ++ * ++ * Attach to Oprofile after qualify the availability of the backend ++ * profiler support. ++ */ ++int __init oprofile_arch_init(struct oprofile_operations *ops) ++{ ++ int r = -ENODEV; ++ ++ profile_node = (struct profilenode *)devtree_find_node("profiler"); ++ ++ if (profile_node == NULL) { ++ printk(KERN_WARNING "Cannot find profiler node\n"); ++ return r; ++ } ++ ++ r = request_irq(profile_node->dn.recvirq, profile_interrupt, ++ IRQF_DISABLED, "profiler", NULL); ++ ++ if (r < 0) { ++ profile_node = NULL; ++ printk(KERN_WARNING "Cannot get profiler IRQ\n"); ++ return r; ++ } ++ ++ ops->start = profile_start; ++ ops->stop = profile_stop; ++ ops->cpu_type = "timer"; ++ ++ memset(cpu_map, 0, sizeof(cpu_map)); ++ ++ on_each_cpu(ubicom32_build_cpu_th_mask, &th_all_mask, 1); ++ ++ memset(®s, 0, sizeof(regs)); ++ ++ return r; ++} ++ ++/* ++ * oprofile_arch_exit() ++ * ++ * External call to take outselves out. ++ * Make sure backend is not running. ++ */ ++void oprofile_arch_exit(void) ++{ ++ BUG_ON(profile_node->enabled); ++} +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -134,3 +134,16 @@ config HW_RANDOM_VIRTIO + + To compile this driver as a module, choose M here: the + module will be called virtio-rng. If unsure, say N. ++ ++config HW_RANDOM_UBICOM32 ++ tristate "Ubicom32 HW Random Number Generator support" ++ depends on HW_RANDOM && UBICOM32 ++ default HW_RANDOM ++ ---help--- ++ This driver provides kernel-side support for the Random Number ++ Generator hardware found on Ubicom32 processors. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called pasemi-rng. ++ ++ If unsure, say Y. +--- a/drivers/char/hw_random/Makefile ++++ b/drivers/char/hw_random/Makefile +@@ -14,3 +14,4 @@ obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx + obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o + obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o + obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o ++obj-$(CONFIG_HW_RANDOM_UBICOM32) += ubicom32-rng.o +--- /dev/null ++++ b/drivers/char/hw_random/ubicom32-rng.c +@@ -0,0 +1,105 @@ ++/* ++ * drivers/net/ubi32-eth.c ++ * Ubicom32 hardware random number generator driver. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/hw_random.h> ++#include <linux/delay.h> ++#include <asm/io.h> ++#include <asm/ip5000.h> ++ ++#define MODULE_NAME "ubicom32_rng" ++ ++static int ubicom32_rng_data_present(struct hwrng *rng, int wait) ++{ ++ int data, i; ++ ++ for (i = 0; i < 20; i++) { ++ data = *(int *)(TIMER_BASE + TIMER_TRN); ++ if (data || !wait) ++ break; ++ udelay(10); ++ } ++ return data; ++} ++ ++static int ubicom32_rng_data_read(struct hwrng *rng, u32 *data) ++{ ++ *data = *(int *)(TIMER_BASE + TIMER_TRN); ++ return 4; ++} ++ ++static int ubicom32_rng_init(struct hwrng *rng) ++{ ++ printk(KERN_INFO "ubicom32 rng init\n"); ++ *(int *)(TIMER_BASE + TIMER_TRN_CFG) = TIMER_TRN_CFG_ENABLE_OSC; ++ return 0; ++} ++ ++static void ubicom32_rng_cleanup(struct hwrng *rng) ++{ ++ printk(KERN_INFO "ubicom32 rng cleanup\n"); ++ *(int *)(TIMER_BASE + TIMER_TRN_CFG) = 0; ++} ++ ++static struct hwrng ubicom32_rng = { ++ .name = MODULE_NAME, ++ .init = ubicom32_rng_init, ++ .cleanup = ubicom32_rng_cleanup, ++ .data_present = ubicom32_rng_data_present, ++ .data_read = ubicom32_rng_data_read, ++ .priv = 0, ++}; ++ ++static int __init mod_init(void) ++{ ++ int err; ++ ++ printk(KERN_INFO "ubicom32 rng started\n"); ++ err = hwrng_register(&ubicom32_rng); ++ if (err) { ++ printk(KERN_ERR "ubicom32 rng register failed (%d)\n", ++ err); ++ } ++ ++ return err; ++} ++ ++static void __exit mod_exit(void) ++{ ++ printk(KERN_INFO "ubicom32 rng stopped\n"); ++ hwrng_unregister(&ubicom32_rng); ++} ++ ++module_init(mod_init); ++module_exit(mod_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ubicom, Inc."); ++MODULE_DESCRIPTION("H/W rng driver for ubicom32 processor"); ++MODULE_VERSION("1:1.0.a"); +--- a/drivers/crypto/Kconfig ++++ b/drivers/crypto/Kconfig +@@ -61,6 +61,40 @@ config CRYPTO_DEV_GEODE + To compile this driver as a module, choose M here: the module + will be called geode-aes. + ++config CRYPTO_UBICOM32 ++ bool "Ubicom32 Security Module" ++ depends on UBICOM32 ++ help ++ This is the ubicom32 hardware acceleration common code. ++ ++config CRYPTO_AES_UBICOM32 ++ tristate "Ubicom32 AES implementation" ++ depends on CRYPTO_UBICOM32 ++ select CRYPTO_ALGAPI ++ help ++ This is the ubicom32 hardware AES implementation. ++ ++config CRYPTO_DES_UBICOM32 ++ tristate "Ubicom32 DES implementation" ++ depends on CRYPTO_UBICOM32 ++ select CRYPTO_ALGAPI ++ help ++ This is the ubicom32 hardware DES and 3DES implementation. ++ ++config CRYPTO_SHA1_UBICOM32 ++ tristate "Ubicom32 SHA1 implementation" ++ depends on CRYPTO_UBICOM32 ++ select CRYPTO_ALGAPI ++ help ++ This is the ubicom32 hardware SHA1 implementation. ++ ++config CRYPTO_MD5_UBICOM32 ++ tristate "Ubicom32 MD5 implementation" ++ depends on CRYPTO_UBICOM32 ++ select CRYPTO_ALGAPI ++ help ++ This is the ubicom32 hardware MD5 implementation. ++ + config ZCRYPT + tristate "Support for PCI-attached cryptographic adapters" + depends on S390 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -193,6 +193,13 @@ config MMC_TMIO + This provides support for the SD/MMC cell found in TC6393XB, + T7L66XB and also ipaq ASIC3 + ++config MMC_UBICOM32 ++ tristate "Ubicom32 MMC/SD host controller" ++ depends on UBICOM32 ++ help ++ This provides support for the SD/MMC hardware found on Ubicom32 ++ IP7K processors ++ + config GPIOMMC + tristate "MMC/SD over GPIO-based SPI" + depends on MMC && MMC_SPI && (SPI_GPIO || SPI_GPIO_OLD) +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -22,5 +22,6 @@ obj-$(CONFIG_MMC_SPI) += mmc_spi.o + obj-$(CONFIG_MMC_S3C) += s3cmci.o + obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o + obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o ++obj-$(CONFIG_MMC_UBICOM32) += ubicom32sd.o + obj-$(CONFIG_GPIOMMC) += gpiommc.o + +--- /dev/null ++++ b/drivers/mmc/host/ubicom32sd.c +@@ -0,0 +1,774 @@ ++/* ++ * drivers/mmc/host/ubicom32sd.c ++ * Ubicom32 Secure Digital Host Controller Interface driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/scatterlist.h> ++#include <linux/leds.h> ++#include <linux/gpio.h> ++#include <linux/mmc/host.h> ++ ++#include <asm/ubicom32sd.h> ++ ++#define DRIVER_NAME "ubicom32sd" ++ ++#define sd_printk(...) ++//#define sd_printk printk ++ ++#define SDTIO_VP_VERSION 3 ++ ++#define SDTIO_MAX_SG_BLOCKS 16 ++ ++enum sdtio_commands { ++ SDTIO_COMMAND_NOP, ++ SDTIO_COMMAND_SETUP, ++ SDTIO_COMMAND_SETUP_SDIO, ++ SDTIO_COMMAND_EXECUTE, ++ SDTIO_COMMAND_RESET, ++}; ++ ++#define SDTIO_COMMAND_SHIFT 24 ++#define SDTIO_COMMAND_FLAG_STOP_RSP_CRC (1 << 10) ++#define SDTIO_COMMAND_FLAG_STOP_RSP_136 (1 << 9) ++#define SDTIO_COMMAND_FLAG_STOP_RSP (1 << 8) ++#define SDTIO_COMMAND_FLAG_STOP_CMD (1 << 7) ++#define SDTIO_COMMAND_FLAG_DATA_STREAM (1 << 6) ++#define SDTIO_COMMAND_FLAG_DATA_RD (1 << 5) ++#define SDTIO_COMMAND_FLAG_DATA_WR (1 << 4) ++#define SDTIO_COMMAND_FLAG_CMD_RSP_CRC (1 << 3) ++#define SDTIO_COMMAND_FLAG_CMD_RSP_136 (1 << 2) ++#define SDTIO_COMMAND_FLAG_CMD_RSP (1 << 1) ++#define SDTIO_COMMAND_FLAG_CMD (1 << 0) ++ ++/* ++ * SDTIO_COMMAND_SETUP_SDIO ++ */ ++#define SDTIO_COMMAND_FLAG_SDIO_INT_EN (1 << 0) ++ ++/* ++ * SDTIO_COMMAND_SETUP ++ * clock speed in arg ++ */ ++#define SDTIO_COMMAND_FLAG_4BIT (1 << 3) ++#define SDTIO_COMMAND_FLAG_1BIT (1 << 2) ++#define SDTIO_COMMAND_FLAG_SET_CLOCK (1 << 1) ++#define SDTIO_COMMAND_FLAG_SET_WIDTH (1 << 0) ++ ++#define SDTIO_COMMAND_FLAG_CMD_RSP_MASK (SDTIO_COMMAND_FLAG_CMD_RSP | SDTIO_COMMAND_FLAG_CMD_RSP_136) ++#define SDTIO_COMMAND_FLAG_STOP_RSP_MASK (SDTIO_COMMAND_FLAG_STOP_RSP | SDTIO_COMMAND_FLAG_STOP_RSP_136) ++#define SDTIO_COMMAND_FLAG_RSP_MASK (SDTIO_COMMAND_FLAG_CMD_RSP_MASK | SDTIO_COMMAND_FLAG_STOP_RSP_MASK) ++ ++struct sdtio_vp_sg { ++ volatile void *addr; ++ volatile u32_t len; ++}; ++ ++#define SDTIO_VP_INT_STATUS_DONE (1 << 31) ++#define SDTIO_VP_INT_STATUS_SDIO_INT (1 << 10) ++#define SDTIO_VP_INT_STATUS_DATA_CRC_ERR (1 << 9) ++#define SDTIO_VP_INT_STATUS_DATA_PROG_ERR (1 << 8) ++#define SDTIO_VP_INT_STATUS_DATA_TIMEOUT (1 << 7) ++#define SDTIO_VP_INT_STATUS_STOP_RSP_CRC (1 << 6) ++#define SDTIO_VP_INT_STATUS_STOP_RSP_TIMEOUT (1 << 5) ++#define SDTIO_VP_INT_STATUS_CMD_RSP_CRC (1 << 4) ++#define SDTIO_VP_INT_STATUS_CMD_RSP_TIMEOUT (1 << 3) ++#define SDTIO_VP_INT_STATUS_CMD_TIMEOUT (1 << 2) ++#define SDTIO_VP_INT_STATUS_CARD1_INSERT (1 << 1) ++#define SDTIO_VP_INT_STATUS_CARD0_INSERT (1 << 0) ++ ++struct sdtio_vp_regs { ++ u32_t version; ++ u32_t f_max; ++ u32_t f_min; ++ ++ volatile u32_t int_status; ++ ++ volatile u32_t command; ++ volatile u32_t arg; ++ ++ volatile u32_t cmd_opcode; ++ volatile u32_t cmd_arg; ++ volatile u32_t cmd_rsp0; ++ volatile u32_t cmd_rsp1; ++ volatile u32_t cmd_rsp2; ++ volatile u32_t cmd_rsp3; ++ ++ volatile u32_t stop_opcode; ++ volatile u32_t stop_arg; ++ volatile u32_t stop_rsp0; ++ volatile u32_t stop_rsp1; ++ volatile u32_t stop_rsp2; ++ volatile u32_t stop_rsp3; ++ ++ volatile u32_t data_timeout_ns; ++ volatile u16_t data_blksz; ++ volatile u16_t data_blkct; ++ volatile u32_t data_bytes_transferred; ++ volatile u32_t sg_len; ++ struct sdtio_vp_sg sg[SDTIO_MAX_SG_BLOCKS]; ++}; ++ ++struct ubicom32sd_data { ++ const struct ubicom32sd_platform_data *pdata; ++ ++ struct mmc_host *mmc; ++ ++ /* ++ * Lock used to protect the data structure ++ spinlock_t lock; ++ */ ++ int int_en; ++ int int_pend; ++ ++ /* ++ * Receive and transmit interrupts used for communicating ++ * with hardware ++ */ ++ int irq_tx; ++ int irq_rx; ++ ++ /* ++ * Current outstanding mmc request ++ */ ++ struct mmc_request *mrq; ++ ++ /* ++ * Hardware registers ++ */ ++ struct sdtio_vp_regs *regs; ++}; ++ ++/*****************************************************************************\ ++ * * ++ * Suspend/resume * ++ * * ++\*****************************************************************************/ ++ ++#if 0//def CONFIG_PM ++ ++int ubicom32sd_suspend_host(struct ubicom32sd_host *host, pm_message_t state) ++{ ++ int ret; ++ ++ ret = mmc_suspend_host(host->mmc, state); ++ if (ret) ++ return ret; ++ ++ free_irq(host->irq, host); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(ubicom32sd_suspend_host); ++ ++int ubicom32sd_resume_host(struct ubicom32sd_host *host) ++{ ++ int ret; ++ ++ if (host->flags & UBICOM32SD_USE_DMA) { ++ if (host->ops->enable_dma) ++ host->ops->enable_dma(host); ++ } ++ ++ ret = request_irq(host->irq, ubicom32sd_irq, IRQF_SHARED, ++ mmc_hostname(host->mmc), host); ++ if (ret) ++ return ret; ++ ++ ubicom32sd_init(host); ++ mmiowb(); ++ ++ ret = mmc_resume_host(host->mmc); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(ubicom32sd_resume_host); ++ ++#endif /* CONFIG_PM */ ++ ++/* ++ * ubicom32sd_send_command_sync ++ */ ++static void ubicom32sd_send_command_sync(struct ubicom32sd_data *ud, u32_t command, u32_t arg) ++{ ++ ud->regs->command = command; ++ ud->regs->arg = arg; ++ ubicom32_set_interrupt(ud->irq_tx); ++ while (ud->regs->command) { ++ ndelay(100); ++ } ++} ++ ++/* ++ * ubicom32sd_send_command ++ */ ++static void ubicom32sd_send_command(struct ubicom32sd_data *ud, u32_t command, u32_t arg) ++{ ++ ud->regs->command = command; ++ ud->regs->arg = arg; ++ ubicom32_set_interrupt(ud->irq_tx); ++} ++ ++/* ++ * ubicom32sd_reset ++ */ ++static void ubicom32sd_reset(struct ubicom32sd_data *ud) ++{ ++ ubicom32sd_send_command_sync(ud, SDTIO_COMMAND_RESET << SDTIO_COMMAND_SHIFT, 0); ++ ud->regs->int_status = 0; ++} ++ ++/* ++ * ubicom32sd_mmc_request ++ */ ++static void ubicom32sd_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct ubicom32sd_data *ud = (struct ubicom32sd_data *)mmc_priv(mmc); ++ u32_t command = SDTIO_COMMAND_EXECUTE << SDTIO_COMMAND_SHIFT; ++ int ret = 0; ++ ++ WARN(ud->mrq != NULL, "ud->mrq still set to %p\n", ud->mrq); ++ //pr_debug("send cmd %08x arg %08x flags %08x\n", cmd->opcode, cmd->arg, cmd->flags); ++ ++ if (mrq->cmd) { ++ struct mmc_command *cmd = mrq->cmd; ++ ++ sd_printk("%s:\t\t\tsetup cmd %02d arg %08x flags %08x\n", mmc_hostname(mmc), cmd->opcode, cmd->arg, cmd->flags); ++ ++ ud->regs->cmd_opcode = cmd->opcode; ++ ud->regs->cmd_arg = cmd->arg; ++ ++ command |= SDTIO_COMMAND_FLAG_CMD; ++ ++ if (cmd->flags & MMC_RSP_PRESENT) { ++ command |= SDTIO_COMMAND_FLAG_CMD_RSP; ++ } ++ ++ if (cmd->flags & MMC_RSP_136) { ++ command |= SDTIO_COMMAND_FLAG_CMD_RSP_136; ++ } ++ ++ if (cmd->flags & MMC_RSP_CRC) { ++ command |= SDTIO_COMMAND_FLAG_CMD_RSP_CRC; ++ } ++ } ++ ++ if (mrq->data) { ++ struct mmc_data *data = mrq->data; ++ struct scatterlist *sg = data->sg; ++ int i; ++ ++printk("%s:\t\t\tsetup data blksz %d num %d sglen=%d fl=%08x Tns=%u\n", mmc_hostname(mmc), data->blksz, data->blocks, data->sg_len, data->flags, data->timeout_ns); ++ ++ sd_printk("%s:\t\t\tsetup data blksz %d num %d sglen=%d fl=%08x Tns=%u\n", ++ mmc_hostname(mmc), data->blksz, data->blocks, data->sg_len, ++ data->flags, data->timeout_ns); ++ ++ if (data->sg_len > SDTIO_MAX_SG_BLOCKS) { ++ ret = -EINVAL; ++ data->error = -EINVAL; ++ goto fail; ++ } ++ ++ ud->regs->data_timeout_ns = data->timeout_ns; ++ ud->regs->data_blksz = data->blksz; ++ ud->regs->data_blkct = data->blocks; ++ ud->regs->sg_len = data->sg_len; ++ ++ /* ++ * Load all of our sg list into the driver sg buffer ++ */ ++ for (i = 0; i < data->sg_len; i++) { ++ sd_printk("%s: sg %d = %p %d\n", mmc_hostname(mmc), i, sg_virt(sg), sg->length); ++ ud->regs->sg[i].addr = sg_virt(sg); ++ ud->regs->sg[i].len = sg->length; ++ if (((u32_t)ud->regs->sg[i].addr & 0x03) || (sg->length & 0x03)) { ++ sd_printk("%s: Need aligned buffers\n", mmc_hostname(mmc)); ++ ret = -EINVAL; ++ data->error = -EINVAL; ++ goto fail; ++ } ++ sg++; ++ } ++ if (data->flags & MMC_DATA_READ) { ++ command |= SDTIO_COMMAND_FLAG_DATA_RD; ++ } else if (data->flags & MMC_DATA_WRITE) { ++ command |= SDTIO_COMMAND_FLAG_DATA_WR; ++ } else if (data->flags & MMC_DATA_STREAM) { ++ command |= SDTIO_COMMAND_FLAG_DATA_STREAM; ++ } ++ } ++ ++ if (mrq->stop) { ++ struct mmc_command *stop = mrq->stop; ++ sd_printk("%s: \t\t\tsetup stop %02d arg %08x flags %08x\n", mmc_hostname(mmc), stop->opcode, stop->arg, stop->flags); ++ ++ ud->regs->stop_opcode = stop->opcode; ++ ud->regs->stop_arg = stop->arg; ++ ++ command |= SDTIO_COMMAND_FLAG_STOP_CMD; ++ ++ if (stop->flags & MMC_RSP_PRESENT) { ++ command |= SDTIO_COMMAND_FLAG_STOP_RSP; ++ } ++ ++ if (stop->flags & MMC_RSP_136) { ++ command |= SDTIO_COMMAND_FLAG_STOP_RSP_136; ++ } ++ ++ if (stop->flags & MMC_RSP_CRC) { ++ command |= SDTIO_COMMAND_FLAG_STOP_RSP_CRC; ++ } ++ } ++ ++ ud->mrq = mrq; ++ ++ sd_printk("%s: Sending command %08x\n", mmc_hostname(mmc), command); ++ ++ ubicom32sd_send_command(ud, command, 0); ++ ++ return; ++fail: ++ sd_printk("%s: mmcreq ret = %d\n", mmc_hostname(mmc), ret); ++ mrq->cmd->error = ret; ++ mmc_request_done(mmc, mrq); ++} ++ ++/* ++ * ubicom32sd_mmc_set_ios ++ */ ++static void ubicom32sd_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct ubicom32sd_data *ud = (struct ubicom32sd_data *)mmc_priv(mmc); ++ u32_t command = SDTIO_COMMAND_SETUP << SDTIO_COMMAND_SHIFT; ++ u32_t arg = 0; ++ sd_printk("%s: ios call bw:%u pm:%u clk:%u\n", mmc_hostname(mmc), 1 << ios->bus_width, ios->power_mode, ios->clock); ++ ++ switch (ios->bus_width) { ++ case MMC_BUS_WIDTH_1: ++ command |= SDTIO_COMMAND_FLAG_SET_WIDTH | SDTIO_COMMAND_FLAG_1BIT; ++ break; ++ ++ case MMC_BUS_WIDTH_4: ++ command |= SDTIO_COMMAND_FLAG_SET_WIDTH | SDTIO_COMMAND_FLAG_4BIT; ++ break; ++ } ++ ++ if (ios->clock) { ++ arg = ios->clock; ++ command |= SDTIO_COMMAND_FLAG_SET_CLOCK; ++ } ++ ++ switch (ios->power_mode) { ++ ++ /* ++ * Turn off the SD bus (power + clock) ++ */ ++ case MMC_POWER_OFF: ++ gpio_set_value(ud->pdata->cards[0].pin_pwr, !ud->pdata->cards[0].pwr_polarity); ++ command |= SDTIO_COMMAND_FLAG_SET_CLOCK; ++ break; ++ ++ /* ++ * Turn on the power to the SD bus ++ */ ++ case MMC_POWER_ON: ++ gpio_set_value(ud->pdata->cards[0].pin_pwr, ud->pdata->cards[0].pwr_polarity); ++ break; ++ ++ /* ++ * Turn on the clock to the SD bus ++ */ ++ case MMC_POWER_UP: ++ /* ++ * Done above ++ */ ++ break; ++ } ++ ++ ubicom32sd_send_command_sync(ud, command, arg); ++ ++ /* ++ * Let the power settle down ++ */ ++ udelay(500); ++} ++ ++/* ++ * ubicom32sd_mmc_get_cd ++ */ ++static int ubicom32sd_mmc_get_cd(struct mmc_host *mmc) ++{ ++ struct ubicom32sd_data *ud = (struct ubicom32sd_data *)mmc_priv(mmc); ++ sd_printk("%s: get cd %u %u\n", mmc_hostname(mmc), ud->pdata->cards[0].pin_cd, gpio_get_value(ud->pdata->cards[0].pin_cd)); ++ ++ return gpio_get_value(ud->pdata->cards[0].pin_cd) ? ++ ud->pdata->cards[0].cd_polarity : ++ !ud->pdata->cards[0].cd_polarity; ++} ++ ++/* ++ * ubicom32sd_mmc_get_ro ++ */ ++static int ubicom32sd_mmc_get_ro(struct mmc_host *mmc) ++{ ++ struct ubicom32sd_data *ud = (struct ubicom32sd_data *)mmc_priv(mmc); ++ sd_printk("%s: get ro %u %u\n", mmc_hostname(mmc), ud->pdata->cards[0].pin_wp, gpio_get_value(ud->pdata->cards[0].pin_wp)); ++ ++ return gpio_get_value(ud->pdata->cards[0].pin_wp) ? ++ ud->pdata->cards[0].wp_polarity : ++ !ud->pdata->cards[0].wp_polarity; ++} ++ ++/* ++ * ubicom32sd_mmc_enable_sdio_irq ++ */ ++static void ubicom32sd_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct ubicom32sd_data *ud = (struct ubicom32sd_data *)mmc_priv(mmc); ++ ++ ud->int_en = enable; ++ if (enable && ud->int_pend) { ++ ud->int_pend = 0; ++ mmc_signal_sdio_irq(mmc); ++ } ++} ++ ++/* ++ * ubicom32sd_interrupt ++ */ ++static irqreturn_t ubicom32sd_interrupt(int irq, void *dev) ++{ ++ struct mmc_host *mmc = (struct mmc_host *)dev; ++ struct mmc_request *mrq; ++ struct ubicom32sd_data *ud; ++ u32_t int_status; ++ ++ if (!mmc) { ++ return IRQ_HANDLED; ++ } ++ ++ ud = (struct ubicom32sd_data *)mmc_priv(mmc); ++ if (!ud) { ++ return IRQ_HANDLED; ++ } ++ ++ int_status = ud->regs->int_status; ++ ud->regs->int_status &= ~int_status; ++ ++ if (int_status & SDTIO_VP_INT_STATUS_SDIO_INT) { ++ if (ud->int_en) { ++ ud->int_pend = 0; ++ mmc_signal_sdio_irq(mmc); ++ } else { ++ ud->int_pend++; ++ } ++ } ++ ++ if (!(int_status & SDTIO_VP_INT_STATUS_DONE)) { ++ return IRQ_HANDLED; ++ } ++ ++ mrq = ud->mrq; ++ if (!mrq) { ++ sd_printk("%s: Spurious interrupt", mmc_hostname(mmc)); ++ return IRQ_HANDLED; ++ } ++ ud->mrq = NULL; ++ ++ /* ++ * SDTIO_VP_INT_DONE ++ */ ++ if (mrq->cmd->flags & MMC_RSP_PRESENT) { ++ struct mmc_command *cmd = mrq->cmd; ++ cmd->error = 0; ++ ++ if ((cmd->flags & MMC_RSP_CRC) && (int_status & SDTIO_VP_INT_STATUS_CMD_RSP_CRC)) { ++ cmd->error = -EILSEQ; ++ } else if (int_status & SDTIO_VP_INT_STATUS_CMD_RSP_TIMEOUT) { ++ cmd->error = -ETIMEDOUT; ++ goto done; ++ } else if (cmd->flags & MMC_RSP_136) { ++ cmd->resp[0] = ud->regs->cmd_rsp0; ++ cmd->resp[1] = ud->regs->cmd_rsp1; ++ cmd->resp[2] = ud->regs->cmd_rsp2; ++ cmd->resp[3] = ud->regs->cmd_rsp3; ++ } else { ++ cmd->resp[0] = ud->regs->cmd_rsp0; ++ } ++ sd_printk("%s:\t\t\tResponse %08x %08x %08x %08x err=%d\n", mmc_hostname(mmc), cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error); ++ } ++ ++ if (mrq->data) { ++ struct mmc_data *data = mrq->data; ++ ++ if (int_status & SDTIO_VP_INT_STATUS_DATA_TIMEOUT) { ++ data->error = -ETIMEDOUT; ++ sd_printk("%s:\t\t\tData Timeout\n", mmc_hostname(mmc)); ++ goto done; ++ } else if (int_status & SDTIO_VP_INT_STATUS_DATA_CRC_ERR) { ++ data->error = -EILSEQ; ++ sd_printk("%s:\t\t\tData CRC\n", mmc_hostname(mmc)); ++ goto done; ++ } else if (int_status & SDTIO_VP_INT_STATUS_DATA_PROG_ERR) { ++ data->error = -EILSEQ; ++ sd_printk("%s:\t\t\tData Program Error\n", mmc_hostname(mmc)); ++ goto done; ++ } else { ++ data->error = 0; ++ data->bytes_xfered = ud->regs->data_bytes_transferred; ++ } ++ } ++ ++ if (mrq->stop && (mrq->stop->flags & MMC_RSP_PRESENT)) { ++ struct mmc_command *stop = mrq->stop; ++ stop->error = 0; ++ ++ if ((stop->flags & MMC_RSP_CRC) && (int_status & SDTIO_VP_INT_STATUS_STOP_RSP_CRC)) { ++ stop->error = -EILSEQ; ++ } else if (int_status & SDTIO_VP_INT_STATUS_STOP_RSP_TIMEOUT) { ++ stop->error = -ETIMEDOUT; ++ goto done; ++ } else if (stop->flags & MMC_RSP_136) { ++ stop->resp[0] = ud->regs->stop_rsp0; ++ stop->resp[1] = ud->regs->stop_rsp1; ++ stop->resp[2] = ud->regs->stop_rsp2; ++ stop->resp[3] = ud->regs->stop_rsp3; ++ } else { ++ stop->resp[0] = ud->regs->stop_rsp0; ++ } ++ sd_printk("%s:\t\t\tStop Response %08x %08x %08x %08x err=%d\n", mmc_hostname(mmc), stop->resp[0], stop->resp[1], stop->resp[2], stop->resp[3], stop->error); ++ } ++ ++done: ++ mmc_request_done(mmc, mrq); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct mmc_host_ops ubicom32sd_ops = { ++ .request = ubicom32sd_mmc_request, ++ .set_ios = ubicom32sd_mmc_set_ios, ++ .get_ro = ubicom32sd_mmc_get_ro, ++ .get_cd = ubicom32sd_mmc_get_cd, ++ .enable_sdio_irq = ubicom32sd_mmc_enable_sdio_irq, ++}; ++ ++/* ++ * ubicom32sd_probe ++ */ ++static int __devinit ubicom32sd_probe(struct platform_device *pdev) ++{ ++ struct ubicom32sd_platform_data *pdata = (struct ubicom32sd_platform_data *)pdev->dev.platform_data; ++ struct mmc_host *mmc; ++ struct ubicom32sd_data *ud; ++ struct resource *res_regs; ++ struct resource *res_irq_tx; ++ struct resource *res_irq_rx; ++ int ret; ++ ++ /* ++ * Get our resources, regs is the hardware driver base address ++ * and the tx and rx irqs are used to communicate with the ++ * hardware driver. ++ */ ++ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ res_irq_tx = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ res_irq_rx = platform_get_resource(pdev, IORESOURCE_IRQ, 1); ++ if (!res_regs || !res_irq_tx || !res_irq_rx) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* ++ * Reserve any gpios we need ++ */ ++ ret = gpio_request(pdata->cards[0].pin_wp, "sd-wp"); ++ if (ret) { ++ goto fail; ++ } ++ gpio_direction_input(pdata->cards[0].pin_wp); ++ ++ ret = gpio_request(pdata->cards[0].pin_cd, "sd-cd"); ++ if (ret) { ++ goto fail_cd; ++ } ++ gpio_direction_input(pdata->cards[0].pin_cd); ++ ++ /* ++ * HACK: for the dual port controller on port F, we don't support the second port right now ++ */ ++ if (pdata->ncards > 1) { ++ ret = gpio_request(pdata->cards[1].pin_pwr, "sd-pwr"); ++ gpio_direction_output(pdata->cards[1].pin_pwr, !pdata->cards[1].pwr_polarity); ++ gpio_direction_output(pdata->cards[1].pin_pwr, pdata->cards[1].pwr_polarity); ++ } ++ ++ ret = gpio_request(pdata->cards[0].pin_pwr, "sd-pwr"); ++ if (ret) { ++ goto fail_pwr; ++ } ++ gpio_direction_output(pdata->cards[0].pin_pwr, !pdata->cards[0].pwr_polarity); ++ ++ /* ++ * Allocate the MMC driver, it includes memory for our data. ++ */ ++ mmc = mmc_alloc_host(sizeof(struct ubicom32sd_data), &pdev->dev); ++ if (!mmc) { ++ ret = -ENOMEM; ++ goto fail_mmc; ++ } ++ ud = (struct ubicom32sd_data *)mmc_priv(mmc); ++ ud->mmc = mmc; ++ ud->pdata = pdata; ++ ud->regs = (struct sdtio_vp_regs *)res_regs->start; ++ ud->irq_tx = res_irq_tx->start; ++ ud->irq_rx = res_irq_rx->start; ++ platform_set_drvdata(pdev, mmc); ++ ++ ret = request_irq(ud->irq_rx, ubicom32sd_interrupt, IRQF_DISABLED, mmc_hostname(mmc), mmc); ++ if (ret) { ++ goto fail_mmc; ++ } ++ ++ /* ++ * Fill in the mmc structure ++ */ ++ mmc->ops = &ubicom32sd_ops; ++ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NEEDS_POLL | MMC_CAP_SDIO_IRQ | ++ MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; ++ ++ mmc->f_min = ud->regs->f_min; ++ mmc->f_max = ud->regs->f_max; ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ /* ++ * Setup some restrictions on transfers ++ * ++ * We allow up to SDTIO_MAX_SG_BLOCKS of data to DMA into, there are ++ * not really any "max_seg_size", "max_req_size", or "max_blk_count" ++ * restrictions (must be less than U32_MAX though), pick ++ * something large?!... ++ * ++ * The hardware can do up to 4095 bytes per block, since the spec ++ * only requires 2048, we'll set it to that and not worry about ++ * potential weird blk lengths. ++ */ ++ mmc->max_hw_segs = SDTIO_MAX_SG_BLOCKS; ++ mmc->max_phys_segs = SDTIO_MAX_SG_BLOCKS; ++ mmc->max_seg_size = 1024 * 1024; ++ mmc->max_req_size = 1024 * 1024; ++ mmc->max_blk_count = 1024; ++ ++ mmc->max_blk_size = 2048; ++ ++ ubicom32sd_reset(ud); ++ ++ /* ++ * enable interrupts ++ */ ++ ud->int_en = 0; ++ ubicom32sd_send_command_sync(ud, SDTIO_COMMAND_SETUP_SDIO << SDTIO_COMMAND_SHIFT | SDTIO_COMMAND_FLAG_SDIO_INT_EN, 0); ++ ++ mmc_add_host(mmc); ++ ++ printk(KERN_INFO "%s at %p, irq %d/%d\n", mmc_hostname(mmc), ++ ud->regs, ud->irq_tx, ud->irq_rx); ++ return 0; ++ ++fail_mmc: ++ gpio_free(pdata->cards[0].pin_pwr); ++fail_pwr: ++ gpio_free(pdata->cards[0].pin_cd); ++fail_cd: ++ gpio_free(pdata->cards[0].pin_wp); ++fail: ++ return ret; ++} ++ ++/* ++ * ubicom32sd_remove ++ */ ++static int __devexit ubicom32sd_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ if (mmc) { ++ struct ubicom32sd_data *ud = (struct ubicom32sd_data *)mmc_priv(mmc); ++ ++ gpio_free(ud->pdata->cards[0].pin_pwr); ++ gpio_free(ud->pdata->cards[0].pin_cd); ++ gpio_free(ud->pdata->cards[0].pin_wp); ++ ++ mmc_remove_host(mmc); ++ mmc_free_host(mmc); ++ } ++ ++ /* ++ * Note that our data is allocated as part of the mmc structure ++ * so we don't need to free it. ++ */ ++ return 0; ++} ++ ++static struct platform_driver ubicom32sd_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = ubicom32sd_probe, ++ .remove = __devexit_p(ubicom32sd_remove), ++#if 0 ++ .suspend = ubicom32sd_suspend, ++ .resume = ubicom32sd_resume, ++#endif ++}; ++ ++/* ++ * ubicom32sd_init ++ */ ++static int __init ubicom32sd_init(void) ++{ ++ return platform_driver_register(&ubicom32sd_driver); ++} ++module_init(ubicom32sd_init); ++ ++/* ++ * ubicom32sd_exit ++ */ ++static void __exit ubicom32sd_exit(void) ++{ ++ platform_driver_unregister(&ubicom32sd_driver); ++} ++module_exit(ubicom32sd_exit); ++ ++MODULE_AUTHOR("Patrick Tjin"); ++MODULE_DESCRIPTION("Ubicom32 Secure Digital Host Controller Interface driver"); ++MODULE_LICENSE("GPL"); ++ +--- a/drivers/mtd/devices/Kconfig ++++ b/drivers/mtd/devices/Kconfig +@@ -104,6 +104,17 @@ config M25PXX_USE_FAST_READ + help + This option enables FAST_READ access supported by ST M25Pxx. + ++config MTD_UBI32_M25P80 ++ tristate "Ubicom processor support for most SPI Flash chips (AT26DF, M25P, W25X, ...)" ++ depends on UBICOM32 ++ default y ++ help ++ This enables access to most modern SPI flash chips, used for ++ program and data storage. Series supported include Atmel AT26DF, ++ Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips ++ are supported as well. See the driver source for the current list, ++ or to add other chips. ++ + config MTD_SLRAM + tristate "Uncached system RAM" + help +--- a/drivers/mtd/devices/Makefile ++++ b/drivers/mtd/devices/Makefile +@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o + obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o + obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o + obj-$(CONFIG_MTD_M25P80) += m25p80.o ++obj-$(CONFIG_MTD_UBI32_M25P80) += ubi32-m25p80.o +--- /dev/null ++++ b/drivers/mtd/devices/ubi32-m25p80.c +@@ -0,0 +1,1066 @@ ++/* ++ * drivers/mtd/devices/ubi32-m25p80.c ++ * NOR flash driver, Ubicom processor internal SPI flash interface. ++ * ++ * This code instantiates the serial flash that contains the ++ * original bootcode. The serial flash start at address 0x60000000 ++ * in both Ubicom32V3 and Ubicom32V4 ISAs. ++ * ++ * This piece of flash is made to appear as a Memory Technology ++ * Device (MTD) with this driver to allow Read/Write/Erase operations. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/mtd/physmap.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/flash.h> ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/mutex.h> ++ ++#include <asm/ip5000.h> ++#include <asm/devtree.h> ++ ++#define UBICOM32_FLASH_BASE 0x60000000 ++#define UBICOM32_FLASH_MAX_SIZE 0x01000000 ++#define UBICOM32_FLASH_START 0x00000000 ++#define UBICOM32_KERNEL_OFFSET 0x00010000 /* The kernel starts after Ubicom ++ * .protect section. */ ++ ++static struct mtd_partition ubicom32_flash_partitions[] = { ++ { ++ .name = "Bootloader", /* Protected Section ++ * Partition */ ++ .size = 0x10000, ++ .offset = UBICOM32_FLASH_START, ++// .mask_flags = MTD_WRITEABLE /* Mark Read-only */ ++ }, ++ { ++ .name = "Kernel", /* Kernel Partition. */ ++ .size = 0, /* this will be set up during ++ * probe stage. At that time we ++ * will know end of linux image ++ * in flash. */ ++ .offset = MTDPART_OFS_APPEND, /* Starts right after Protected ++ * section. */ ++// .mask_flags = MTD_WRITEABLE /* Mark Read-only */ ++ }, ++ { ++ .name = "Rest", /* Rest of the flash. */ ++ .size = 0x200000, /* Use up what remains in the ++ * flash. */ ++ .offset = MTDPART_OFS_NXTBLK, /* Starts right after Protected ++ * section. */ ++ } ++}; ++ ++static struct flash_platform_data ubicom32_flash_data = { ++ .name = "ubicom32_boot_flash", ++ .parts = ubicom32_flash_partitions, ++ .nr_parts = ARRAY_SIZE(ubicom32_flash_partitions), ++}; ++ ++static struct resource ubicom32_flash_resource[] = { ++ { ++ .start = UBICOM32_FLASH_BASE, ++ .end = UBICOM32_FLASH_BASE + ++ UBICOM32_FLASH_MAX_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device ubicom32_flash_device = { ++ .name = "ubicom32flashdriver", ++ .id = 0, /* Bus number */ ++ .num_resources = ARRAY_SIZE(ubicom32_flash_resource), ++ .resource = ubicom32_flash_resource, ++ .dev = { ++ .platform_data = &ubicom32_flash_data, ++ }, ++}; ++ ++static struct platform_device *ubicom32_flash_devices[] = { ++ &ubicom32_flash_device, ++}; ++ ++static int __init ubicom32_flash_init(void) ++{ ++ printk(KERN_INFO "%s(): registering device resources\n", ++ __FUNCTION__); ++ platform_add_devices(ubicom32_flash_devices, ++ ARRAY_SIZE(ubicom32_flash_devices)); ++ return 0; ++} ++ ++arch_initcall(ubicom32_flash_init); ++ ++/* ++ * MTD SPI driver for ST M25Pxx (and similar) serial flash chips through ++ * Ubicom32 SPI controller. ++ * ++ * Author: Mike Lavender, mike@steroidmicros.com ++ * ++ * Copyright (c) 2005, Intec Automation Inc. ++ * ++ * Some parts are based on lart.c by Abraham Van Der Merwe ++ * ++ * Cleaned up and generalized based on mtd_dataflash.c ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#define FLASH_PAGESIZE 256 ++ ++/* Flash opcodes. */ ++#define OPCODE_WREN 0x06 /* Write enable */ ++#define OPCODE_RDSR 0x05 /* Read status register */ ++#define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ ++#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ ++#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ ++#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ ++#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ ++#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ ++#define OPCODE_RDID 0x9f /* Read JEDEC ID */ ++ ++/* Status Register bits. */ ++#define SR_WIP 1 /* Write in progress */ ++#define SR_WEL 2 /* Write enable latch */ ++/* meaning of other SR_* bits may differ between vendors */ ++#define SR_BP0 4 /* Block protect 0 */ ++#define SR_BP1 8 /* Block protect 1 */ ++#define SR_BP2 0x10 /* Block protect 2 */ ++#define SR_SRWD 0x80 /* SR write protect */ ++ ++/* Define max times to check status register before we give up. */ ++#define MAX_READY_WAIT_COUNT 100000 ++ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++#define mtd_has_partitions() (1) ++#else ++#define mtd_has_partitions() (0) ++#endif ++ ++/* ++ * Ubicom32 FLASH Command Set ++ */ ++#define FLASH_FC_INST_CMD 0x00 /* for SPI command only transaction */ ++#define FLASH_FC_INST_WR 0x01 /* for SPI write transaction */ ++#define FLASH_FC_INST_RD 0x02 /* for SPI read transaction */ ++ ++#define ALIGN_DOWN(v, a) ((v) & ~((a) - 1)) ++#define ALIGN_UP(v, a) (((v) + ((a) - 1)) & ~((a) - 1)) ++ ++#define FLASH_COMMAND_KICK_OFF(io) \ ++ asm volatile( \ ++ " bset "D(IO_INT_CLR)"(%0), #0, #%%bit("D(IO_XFL_INT_DONE)") \n\t" \ ++ " jmpt.t .+4 \n\t" \ ++ " bset "D(IO_INT_SET)"(%0), #0, #%%bit("D(IO_XFL_INT_START)") \n\t" \ ++ : \ ++ : "a" (io) \ ++ : "memory", "cc" \ ++ ); ++ ++#define FLASH_COMMAND_WAIT_FOR_COMPLETION(io) \ ++ asm volatile( \ ++ " btst "D(IO_INT_STATUS)"(%0), #%%bit("D(IO_XFL_INT_DONE)") \n\t" \ ++ " jmpeq.f .-4 \n\t" \ ++ : \ ++ : "a" (io) \ ++ : "memory", "cc" \ ++ ); ++ ++#define FLASH_COMMAND_EXEC(io) \ ++ FLASH_COMMAND_KICK_OFF(io) \ ++ FLASH_COMMAND_WAIT_FOR_COMPLETION(io) ++ ++ ++#define OSC1_FREQ 12000000 ++#define TEN_MICRO_SECONDS (OSC1_FREQ * 10 / 1000000) ++ ++/* ++ * We will have to eventually replace this null definition with the real thing. ++ */ ++#define WATCHDOG_RESET() ++ ++#define EXTFLASH_WRITE_FIFO_SIZE 32 ++#define EXTFLASH_WRITE_BLOCK_SIZE EXTFLASH_WRITE_FIFO_SIZE /* limit the size to ++ * FIFO capacity, so ++ * the thread can be ++ * suspended. */ ++ ++#define JFFS2_FILESYSTEM_SIZE 0x100000 ++ ++/****************************************************************************/ ++ ++struct m25p { ++ struct platform_device *plt_dev; ++ struct mutex lock; ++ struct mtd_info mtd; ++ unsigned partitioned:1; ++ u8 erase_opcode; ++ u8 command[4]; ++}; ++ ++static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) ++{ ++ return container_of(mtd, struct m25p, mtd); ++} ++ ++/****************************************************************************/ ++ ++/* ++ * Internal helper functions ++ */ ++ ++/* ++ * Read the status register, returning its value in the location ++ * Return the status register value. ++ * Returns negative if error occurred. ++ */ ++static int read_sr(struct m25p *flash) ++{ ++ struct ubicom32_io_port *io = (struct ubicom32_io_port *)RA; ++ ++ io->ctl1 &= ~IO_XFL_CTL1_MASK; ++ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_RD) | ++ IO_XFL_CTL1_FC_DATA(1); ++ io->ctl2 = IO_XFL_CTL2_FC_CMD(OPCODE_RDSR); ++ FLASH_COMMAND_EXEC(io); ++ ++ return io->status1 & 0xff; ++} ++ ++/* ++ * mem_flash_io_read_u32() ++ */ ++static u32 mem_flash_io_read_u32(u32 addr) ++{ ++ struct ubicom32_io_port *io = (struct ubicom32_io_port *)RA; ++ io->ctl1 &= ~IO_XFL_CTL1_MASK; ++ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_RD) | ++ IO_XFL_CTL1_FC_DATA(4) | IO_XFL_CTL1_FC_DUMMY(1) | ++ IO_XFL_CTL1_FC_ADDR; ++ io->ctl2 = IO_XFL_CTL2_FC_CMD(OPCODE_FAST_READ) | ++ IO_XFL_CTL2_FC_ADDR(addr); ++ FLASH_COMMAND_EXEC(io); ++ return io->status1; ++} ++ ++/* ++ * mem_flash_read_u8() ++ */ ++static u8 mem_flash_read_u8(u32 addr) ++{ ++ u32 tmp_addr = ALIGN_DOWN(addr, 4); ++ u32 tmp_data = mem_flash_io_read_u32(tmp_addr); ++ u8 *ptr = (u8 *)&tmp_data; ++ return ptr[addr & 0x3]; ++} ++ ++/* ++ * mem_flash_read() ++ * No need to lock as read is implemented with ireads (same as normal flash ++ * execution). ++ */ ++static void mem_flash_read(u32 addr, void *dst, size_t length) ++{ ++ /* ++ * Range check ++ */ ++ /* ++ * Fix source alignment. ++ */ ++ while (addr & 0x03) { ++ if (length == 0) { ++ return; ++ } ++ *((u8 *)dst) = mem_flash_read_u8(addr++); ++ dst++; ++ length--; ++ } ++ ++ while (length >= 4) { ++ u32 tmp_data = mem_flash_io_read_u32(addr); ++ addr += 4; ++ length -= 4; ++ ++ /* ++ * Send the data to the destination. ++ */ ++ memcpy((void *)dst, (void *)&tmp_data, 4); ++ dst += 4; ++ } ++ ++ while (length--) { ++ *((u8 *)dst) = mem_flash_read_u8(addr++); ++ dst++; ++ } ++} ++ ++/* ++ * mem_flash_wait_until_complete() ++ */ ++static void mem_flash_wait_until_complete(void) ++{ ++ struct ubicom32_io_port *io = (struct ubicom32_io_port *)RA; ++ ++ do { ++ /* ++ * Put a delay here to deal with flash programming problem. ++ */ ++ u32 mptval = UBICOM32_IO_TIMER->mptval + TEN_MICRO_SECONDS; ++ while (UBICOM32_IO_TIMER->mptval < mptval) ++ ; ++ ++ io->ctl1 &= ~IO_XFL_CTL1_MASK; ++ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_RD) | ++ IO_XFL_CTL1_FC_DATA(1); ++ io->ctl2 = IO_XFL_CTL2_FC_CMD(OPCODE_RDSR); ++ FLASH_COMMAND_EXEC(io); ++ } while (io->status1 & SR_WIP); ++} ++ ++/* ++ * mem_flash_write_next() ++ */ ++static size_t mem_flash_write_next(u32 addr, u8 *buf, size_t length) ++{ ++ struct ubicom32_io_port *io = (struct ubicom32_io_port *)RA; ++ u32 data_start = addr; ++ u32 data_end = addr + length; ++ size_t count; ++ u32 i, j; ++ ++ /* ++ * Top limit address. ++ */ ++ u32 block_start = ALIGN_DOWN(data_start, 4); ++ u32 block_end = block_start + EXTFLASH_WRITE_BLOCK_SIZE; ++ ++ union { ++ u8 byte[EXTFLASH_WRITE_BLOCK_SIZE]; ++ u32 word[EXTFLASH_WRITE_BLOCK_SIZE / 4]; ++ } write_buf; ++ ++ u32 *flash_addr = (u32 *)block_start; ++ ++ /* ++ * The write block must be limited by FLASH internal buffer. ++ */ ++ u32 block_end_align = ALIGN_DOWN(block_end, 256); ++ bool write_needed; ++ ++ block_end = (block_end_align > block_start) ++ ? block_end_align : block_end; ++ data_end = (data_end <= block_end) ? data_end : block_end; ++ block_end = ALIGN_UP(data_end, 4); ++ count = data_end - data_start; ++ ++ /* ++ * Transfer data to a buffer. ++ */ ++ for (i = 0; i < (block_end - block_start) / 4; i++) { ++ /* ++ * The FLASH read can hold D-cache for a long time. ++ * Use I/O operation to read FLASH to avoid starving other ++ * threads, especially HRT. (Do this for application only) ++ */ ++ write_buf.word[i] = mem_flash_io_read_u32( ++ (u32)(&flash_addr[i])); ++ } ++ ++ write_needed = false; ++ for (i = 0, j = (data_start - block_start); ++ i < (data_end - data_start); i++, j++) { ++ write_needed = write_needed || (write_buf.byte[j] != buf[i]); ++ write_buf.byte[j] &= buf[i]; ++ } ++ ++ ++ /* ++ * If the data in FLASH is identical to what to be written. Then skip ++ * it. ++ */ ++ if (write_needed) { ++ /* ++ * Write to flash. ++ */ ++ void *tmp __attribute__((unused)); ++ s32 extra_words; ++ ++ asm volatile( ++ " move.4 %0, %2 \n\t" ++ " bset "D(IO_INT_SET)"(%1), #0, #%%bit("D(IO_PORTX_INT_FIFO_TX_RESET)") \n\t" ++ " pipe_flush 0 \n\t" ++ " .rept "D(EXTFLASH_WRITE_FIFO_SIZE / 4)" \n\t" ++ " move.4 "D(IO_TX_FIFO)"(%1), (%0)4++ \n\t" ++ " .endr \n\t" ++ : "=&a" (tmp) ++ : "a" (io), "r" (&write_buf.word[0]) ++ : "memory", "cc" ++ ); ++ ++ /* Lock FLASH for write access. */ ++ io->ctl0 |= IO_XFL_CTL0_MCB_LOCK; ++ ++ /* Command: WREN */ ++ io->ctl1 &= ~IO_XFL_CTL1_MASK; ++ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD); ++ io->ctl2 = IO_XFL_CTL2_FC_CMD(OPCODE_WREN); ++ FLASH_COMMAND_EXEC(io); ++ ++ /* Command: BYTE PROGRAM */ ++ io->ctl1 &= ~IO_XFL_CTL1_MASK; ++ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | ++ IO_XFL_CTL1_FC_DATA(block_end - block_start) | ++ IO_XFL_CTL1_FC_ADDR; ++ io->ctl2 = IO_XFL_CTL2_FC_CMD(OPCODE_PP) | ++ IO_XFL_CTL2_FC_ADDR(block_start); ++ FLASH_COMMAND_KICK_OFF(io); ++ ++ extra_words = (s32)(block_end - block_start - ++ EXTFLASH_WRITE_FIFO_SIZE) / 4; ++ if (extra_words > 0) { ++ asm volatile( ++ " move.4 %0, %3 \n\t" ++ "1: cmpi "D(IO_FIFO_LEVEL)"(%1), #4 \n\t" ++ " jmpgt.s.t 1b \n\t" ++ " move.4 "D(IO_TX_FIFO)"(%1), (%0)4++ \n\t" ++ " add.4 %2, #-1, %2 \n\t" ++ " jmpgt.t 1b \n\t" ++ : "=&a" (tmp) ++ : "a" (io), "d" (extra_words), ++ "r" (&write_buf.word[EXTFLASH_WRITE_FIFO_SIZE / 4]) ++ : "memory", "cc" ++ ); ++ } ++ FLASH_COMMAND_WAIT_FOR_COMPLETION(io); ++ ++ mem_flash_wait_until_complete(); ++ ++ ++ /* Unlock FLASH for cache access. */ ++ io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; ++ } ++ ++ /* ++ * Complete. ++ */ ++ return count; ++} ++ ++/* ++ * mem_flash_write() ++ */ ++static void mem_flash_write(u32 addr, const void *src, size_t length) ++{ ++ /* ++ * Write data ++ */ ++ u8_t *ptr = (u8_t *)src; ++ while (length) { ++ size_t count = mem_flash_write_next(addr, ptr, length); ++ addr += count; ++ ptr += count; ++ length -= count; ++ } ++} ++ ++/* ++ * Service routine to read status register until ready, or timeout occurs. ++ * Returns non-zero if error. ++ */ ++static int wait_till_ready(struct m25p *flash) ++{ ++ int count; ++ int sr; ++ ++ /* one chip guarantees max 5 msec wait here after page writes, ++ * but potentially three seconds (!) after page erase. ++ */ ++ for (count = 0; count < MAX_READY_WAIT_COUNT; count++) { ++ u32 mptval; ++ sr = read_sr(flash); ++ if (sr < 0) ++ break; ++ else if (!(sr & SR_WIP)) ++ return 0; ++ ++ /* ++ * Put a 10us delay here to deal with flash programming problem. ++ */ ++ mptval = UBICOM32_IO_TIMER->mptval + TEN_MICRO_SECONDS; ++ while ((s32)(mptval - UBICOM32_IO_TIMER->mptval) > 0) { ++ WATCHDOG_RESET(); ++ } ++ /* REVISIT sometimes sleeping would be best */ ++ } ++ ++ return 1; ++} ++ ++/* ++ * mem_flash_erase_page() ++ */ ++static void mem_flash_erase_page(u32 addr) ++{ ++ struct ubicom32_io_port *io = (struct ubicom32_io_port *)RA; ++ ++ /* Lock FLASH for write access. */ ++ io->ctl0 |= IO_XFL_CTL0_MCB_LOCK; ++ ++ /* Command: WREN */ ++ io->ctl1 &= ~IO_XFL_CTL1_MASK; ++ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD); ++ io->ctl2 = IO_XFL_CTL2_FC_CMD(OPCODE_WREN); ++ FLASH_COMMAND_EXEC(io); ++ ++ /* Command: ERASE */ ++ io->ctl1 &= ~IO_XFL_CTL1_MASK; ++ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD) | ++ IO_XFL_CTL1_FC_ADDR; ++ io->ctl2 = IO_XFL_CTL2_FC_CMD(OPCODE_SE) | ++ IO_XFL_CTL2_FC_ADDR(addr); ++ FLASH_COMMAND_EXEC(io); ++ ++ mem_flash_wait_until_complete(); ++ ++ /* Unlock FLASH for cache access. */ ++ io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; ++} ++ ++/* ++ * mem_flash_erase() ++ */ ++static u32 mem_flash_erase(u32 addr, u32 length) ++{ ++ /* ++ * Calculate the endaddress to be the first address of the page ++ * just beyond this erase section of pages. ++ */ ++ u32 endaddr = addr + length; ++ ++ /* ++ * Erase. ++ */ ++ while (addr < endaddr) { ++ u32 test_addr = addr; ++ mem_flash_erase_page(addr); ++ ++ /* ++ * Test how much was erased as actual flash page at this address ++ * may be smaller than the expected page size. ++ */ ++ while (test_addr < endaddr) { ++ /* ++ * The FLASH read can hold D-cache for a long time. Use ++ * I/O operation to read FLASH to avoid starving other ++ * threads, especially HRT. (Do this for application ++ * only) ++ */ ++ if (mem_flash_io_read_u32(test_addr) != 0xFFFFFFFF) { ++ break; ++ } ++ test_addr += 4; ++ } ++ if (test_addr == addr) { ++ printk("erase failed at address 0x%x, skipping", ++ test_addr); ++ test_addr += 4; ++ return 1; ++ } ++ addr = test_addr; ++ } ++ return 0; ++} ++ ++ ++/****************************************************************************/ ++ ++/* ++ * MTD implementation ++ */ ++ ++/* ++ * Erase an address range on the flash chip. The address range may extend ++ * one or more erase sectors. Return an error is there is a problem erasing. ++ */ ++static int ubicom32_flash_driver_erase(struct mtd_info *mtd, ++ struct erase_info *instr) ++{ ++ struct m25p *flash = mtd_to_m25p(mtd); ++ u32 addr, len; ++ ++ DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", ++ flash->plt_dev->dev.bus_id, __FUNCTION__, "at", ++ (u32)instr->addr, instr->len); ++ ++ /* sanity checks */ ++ if (instr->addr + instr->len > flash->mtd.size) ++ return -EINVAL; ++ if ((instr->addr % mtd->erasesize) != 0 ++ || (instr->len % mtd->erasesize) != 0) { ++ return -EINVAL; ++ } ++ ++ addr = instr->addr + UBICOM32_FLASH_BASE; ++ len = instr->len; ++ ++ mutex_lock(&flash->lock); ++ ++ /* REVISIT in some cases we could speed up erasing large regions ++ * by using OPCODE_SE instead of OPCODE_BE_4K ++ */ ++ ++ /* now erase those sectors */ ++ if (mem_flash_erase(addr, len)) { ++ instr->state = MTD_ERASE_FAILED; ++ mutex_unlock(&flash->lock); ++ return -EIO; ++ } ++ ++ mutex_unlock(&flash->lock); ++ instr->state = MTD_ERASE_DONE; ++ mtd_erase_callback(instr); ++ return 0; ++} ++ ++/* ++ * Read an address range from the flash chip. The address range ++ * may be any size provided it is within the physical boundaries. ++ */ ++static int ubicom32_flash_driver_read(struct mtd_info *mtd, loff_t from, ++ size_t len, size_t *retlen, u_char *buf) ++{ ++ struct m25p *flash = mtd_to_m25p(mtd); ++ u32 base_addr = UBICOM32_FLASH_BASE + from; ++ ++ DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", ++ flash->plt_dev->dev.bus_id, __FUNCTION__, "from", ++ (u32)from, len); ++ ++ /* sanity checks */ ++ if (!len) ++ return 0; ++ ++ if (from + len > flash->mtd.size) ++ return -EINVAL; ++ ++ /* Byte count starts at zero. */ ++ if (retlen) ++ *retlen = 0; ++ ++ mutex_lock(&flash->lock); ++ ++ /* Wait till previous write/erase is done. */ ++ if (wait_till_ready(flash)) { ++ /* REVISIT status return?? */ ++ mutex_unlock(&flash->lock); ++ return 1; ++ } ++ ++ mem_flash_read(base_addr, (void *)buf, len); ++ ++ if (retlen) ++ *retlen = len; ++ ++ mutex_unlock(&flash->lock); ++ ++ return 0; ++} ++ ++/* ++ * Write an address range to the flash chip. Data must be written in ++ * FLASH_PAGESIZE chunks. The address range may be any size provided ++ * it is within the physical boundaries. ++ */ ++static int ubicom32_flash_driver_write(struct mtd_info *mtd, loff_t to, ++ size_t len, size_t *retlen, ++ const u_char *buf) ++{ ++ struct m25p *flash = mtd_to_m25p(mtd); ++ u32 base_addr = UBICOM32_FLASH_BASE + to; ++ DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", ++ flash->plt_dev->dev.bus_id, __FUNCTION__, "to", ++ (u32)to, len); ++ ++ if (retlen) ++ *retlen = 0; ++ ++ /* sanity checks */ ++ if (!len) ++ return 0; ++ ++ if (to + len > flash->mtd.size) ++ return -EINVAL; ++ ++ mutex_lock(&flash->lock); ++ ++ mem_flash_write(base_addr, (void *) buf, len); ++ ++ /* Wait until finished previous write command. */ ++ if (wait_till_ready(flash)) { ++ mutex_unlock(&flash->lock); ++ return 1; ++ } ++ ++ if (retlen) ++ *retlen = len; ++ ++ mutex_unlock(&flash->lock); ++ return 0; ++} ++ ++ ++/****************************************************************************/ ++ ++/* ++ * SPI device driver setup and teardown ++ */ ++ ++struct flash_info { ++ char *name; ++ ++ /* JEDEC id zero means "no ID" (most older chips); otherwise it has ++ * a high byte of zero plus three data bytes: the manufacturer id, ++ * then a two byte device id. ++ */ ++ u32 jedec_id; ++ ++ /* The size listed here is what works with OPCODE_SE, which isn't ++ * necessarily called a "sector" by the vendor. ++ */ ++ unsigned sector_size; ++ u16 n_sectors; ++ ++ u16 flags; ++#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ ++}; ++ ++ ++/* NOTE: double check command sets and memory organization when you add ++ * more flash chips. This current list focusses on newer chips, which ++ * have been converging on command sets which including JEDEC ID. ++ */ ++static struct flash_info __devinitdata m25p_data[] = { ++ ++ /* Atmel -- some are (confusingly) marketed as "DataFlash" */ ++ { "at25fs010", 0x1f6601, 32 * 1024, 4, SECT_4K, }, ++ { "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, }, ++ ++ { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, }, ++ ++ { "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, }, ++ { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, }, ++ { "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, }, ++ { "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, }, ++ ++ /* Spansion -- single (large) sector size only, at least ++ * for the chips listed here (without boot sectors). ++ */ ++ { "s25sl004a", 0x010212, 64 * 1024, 8, }, ++ { "s25sl008a", 0x010213, 64 * 1024, 16, }, ++ { "s25sl016a", 0x010214, 64 * 1024, 32, }, ++ { "s25sl032a", 0x010215, 64 * 1024, 64, }, ++ { "s25sl064a", 0x010216, 64 * 1024, 128, }, ++ ++ /* SST -- large erase sizes are "overlays", "sectors" are 4K */ ++ { "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, }, ++ { "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, }, ++ { "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, }, ++ { "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, }, ++ ++ /* ST Microelectronics -- newer production may have feature updates */ ++ { "m25p05", 0x202010, 32 * 1024, 2, }, ++ { "m25p10", 0x202011, 32 * 1024, 4, }, ++ { "m25p20", 0x202012, 64 * 1024, 4, }, ++ { "m25p40", 0x202013, 64 * 1024, 8, }, ++ { "m25p80", 0, 64 * 1024, 16, }, ++ { "m25p16", 0x202015, 64 * 1024, 32, }, ++ { "m25p32", 0x202016, 64 * 1024, 64, }, ++ { "m25p64", 0x202017, 64 * 1024, 128, }, ++ { "m25p128", 0x202018, 256 * 1024, 64, }, ++ ++ { "m45pe80", 0x204014, 64 * 1024, 16, }, ++ { "m45pe16", 0x204015, 64 * 1024, 32, }, ++ ++ { "m25pe80", 0x208014, 64 * 1024, 16, }, ++ { "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, }, ++ ++ /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ ++ { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, }, ++ { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, }, ++ { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, }, ++ { "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, }, ++ { "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, }, ++ { "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, }, ++ { "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, }, ++ ++ /* Macronix -- mx25lxxx */ ++ { "mx25l32", 0xc22016, 64 * 1024, 64, }, ++ { "mx25l64", 0xc22017, 64 * 1024, 128, }, ++ { "mx25l128", 0xc22018, 64 * 1024, 256, }, ++ ++}; ++ ++struct flash_info *__devinit jedec_probe(struct platform_device *spi) ++{ ++ int tmp; ++ u32 jedec; ++ struct flash_info *info; ++ struct ubicom32_io_port *io = (struct ubicom32_io_port *)RA; ++ ++ /* ++ * Setup and run RDID command on the flash. ++ */ ++ io->ctl1 &= ~IO_XFL_CTL1_MASK; ++ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_RD) | ++ IO_XFL_CTL1_FC_DATA(3); ++ io->ctl2 = IO_XFL_CTL2_FC_CMD(OPCODE_RDID); ++ FLASH_COMMAND_EXEC(io); ++ ++ jedec = io->status1 & 0x00ffffff; ++ ++ for (tmp = 0, info = m25p_data; ++ tmp < ARRAY_SIZE(m25p_data); ++ tmp++, info++) { ++ if (info->jedec_id == jedec) ++ return info; ++ } ++ dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); ++ return NULL; ++} ++ ++ ++/* ++ * board specific setup should have ensured the SPI clock used here ++ * matches what the READ command supports, at least until this driver ++ * understands FAST_READ (for clocks over 25 MHz). ++ */ ++static int __devinit ubicom32_flash_probe(struct platform_device *spi) ++{ ++ struct flash_platform_data *data; ++ struct m25p *flash; ++ struct flash_info *info; ++ unsigned i; ++ ++ /* Platform data helps sort out which chip type we have, as ++ * well as how this board partitions it. If we don't have ++ * a chip ID, try the JEDEC id commands; they'll work for most ++ * newer chips, even if we don't recognize the particular chip. ++ */ ++ data = spi->dev.platform_data; ++ if (data && data->type) { ++ for (i = 0, info = m25p_data; ++ i < ARRAY_SIZE(m25p_data); ++ i++, info++) { ++ if (strcmp(data->type, info->name) == 0) ++ break; ++ } ++ ++ /* unrecognized chip? */ ++ if (i == ARRAY_SIZE(m25p_data)) { ++ DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", ++ spi->dev.bus_id, data->type); ++ info = NULL; ++ ++ /* recognized; is that chip really what's there? */ ++ } else if (info->jedec_id) { ++ struct flash_info *chip = jedec_probe(spi); ++ ++ if (!chip || chip != info) { ++ dev_warn(&spi->dev, "found %s, expected %s\n", ++ chip ? chip->name : "UNKNOWN", ++ info->name); ++ info = NULL; ++ } ++ } ++ } else ++ info = jedec_probe(spi); ++ ++ if (!info) ++ return -ENODEV; ++ ++ flash = kzalloc(sizeof *flash, GFP_KERNEL); ++ if (!flash) ++ return -ENOMEM; ++ ++ flash->plt_dev = spi; ++ mutex_init(&flash->lock); ++ dev_set_drvdata(&spi->dev, flash); ++ ++ if (data && data->name) ++ flash->mtd.name = data->name; ++ else ++ flash->mtd.name = spi->dev.bus_id; ++ ++ flash->mtd.type = MTD_NORFLASH; ++ flash->mtd.writesize = 1; ++ flash->mtd.flags = MTD_CAP_NORFLASH; ++ flash->mtd.size = info->sector_size * info->n_sectors; ++ flash->mtd.erase = ubicom32_flash_driver_erase; ++ flash->mtd.read = ubicom32_flash_driver_read; ++ flash->mtd.write = ubicom32_flash_driver_write; ++ ++ /* prefer "small sector" erase if possible */ ++ /* ++ * The Ubicom erase code does not use the opcode for smaller sectors, ++ * so disable that functionality and keep erasesize == sector_size ++ * so that the test in ubicom32_flash_driver_erase works properly. ++ * ++ * This was: `if (info->flags & SECT_4K) {' instead of `if (0) {' ++ */ ++ if (0) { ++ flash->erase_opcode = OPCODE_BE_4K; ++ flash->mtd.erasesize = 4096; ++ } else { ++ flash->erase_opcode = OPCODE_SE; ++ flash->mtd.erasesize = info->sector_size; ++ } ++ ++ dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, ++ flash->mtd.size / 1024); ++ ++ DEBUG(MTD_DEBUG_LEVEL2, ++ "mtd .name = %s, .size = 0x%.8x (%uMiB) " ++ ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", ++ flash->mtd.name, ++ flash->mtd.size, flash->mtd.size / (1024*1024), ++ flash->mtd.erasesize, flash->mtd.erasesize / 1024, ++ flash->mtd.numeraseregions); ++ ++ if (flash->mtd.numeraseregions) ++ for (i = 0; i < flash->mtd.numeraseregions; i++) ++ DEBUG(MTD_DEBUG_LEVEL2, ++ "mtd.eraseregions[%d] = { .offset = 0x%.8x, " ++ ".erasesize = 0x%.8x (%uKiB), " ++ ".numblocks = %d }\n", ++ i, flash->mtd.eraseregions[i].offset, ++ flash->mtd.eraseregions[i].erasesize, ++ flash->mtd.eraseregions[i].erasesize / 1024, ++ flash->mtd.eraseregions[i].numblocks); ++ ++ ++ /* partitions should match sector boundaries; and it may be good to ++ * use readonly partitions for writeprotected sectors (BP2..BP0). ++ */ ++ if (mtd_has_partitions()) { ++ struct mtd_partition *parts = NULL; ++ int nr_parts = 0; ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ static const char *part_probes[] = { "cmdlinepart", NULL, }; ++ ++ nr_parts = parse_mtd_partitions(&flash->mtd, ++ part_probes, &parts, 0); ++#endif ++ ++ if (nr_parts <= 0 && data && data->parts) { ++ parts = data->parts; ++ nr_parts = data->nr_parts; ++ if (nr_parts >= 2) { ++ /* ++ * Set last partition size to be 1M. ++ */ ++ parts[1].size = flash->mtd.size - ++ parts[0].size - JFFS2_FILESYSTEM_SIZE; ++ parts[2].size = JFFS2_FILESYSTEM_SIZE; ++ } ++ } ++ ++ if (nr_parts > 0) { ++ for (i = 0; i < nr_parts; i++) { ++ DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " ++ "{.name = %s, .offset = 0x%.8x, " ++ ".size = 0x%.8x (%uKiB) }\n", ++ i, parts[i].name, ++ parts[i].offset, ++ parts[i].size, ++ parts[i].size / 1024); ++ } ++ flash->partitioned = 1; ++ return add_mtd_partitions(&flash->mtd, parts, nr_parts); ++ } ++ } else if (data->nr_parts) ++ dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", ++ data->nr_parts, data->name); ++ ++ return add_mtd_device(&flash->mtd) == 1 ? -ENODEV : 0; ++} ++ ++ ++static int __devexit ubicom32_flash_remove(struct spi_device *spi) ++{ ++ struct m25p *flash = dev_get_drvdata(&spi->dev); ++ int status; ++ ++ /* Clean up MTD stuff. */ ++ if (mtd_has_partitions() && flash->partitioned) ++ status = del_mtd_partitions(&flash->mtd); ++ else ++ status = del_mtd_device(&flash->mtd); ++ if (status == 0) ++ kfree(flash); ++ return 0; ++} ++ ++static struct platform_driver ubicom32_flash_driver = { ++ .driver = { ++ .name = "ubicom32flashdriver", ++ .bus = &platform_bus_type, ++ .owner = THIS_MODULE, ++ }, ++ .probe = ubicom32_flash_probe, ++ .remove = NULL, ++}; ++ ++static int ubicom32_flash_driver_init(void) ++{ ++ return platform_driver_register(&ubicom32_flash_driver); ++} ++ ++ ++static void ubicom32_flash_driver_exit(void) ++{ ++ platform_driver_unregister(&ubicom32_flash_driver); ++} ++ ++ ++module_init(ubicom32_flash_driver_init); ++module_exit(ubicom32_flash_driver_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Mike Lavender"); ++MODULE_DESCRIPTION("Ubicom32 MTD SPI driver for ST M25Pxx flash chips"); +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -2463,6 +2463,25 @@ config JME + To compile this driver as a module, choose M here. The module + will be called jme. + ++config UBICOM32_GMAC ++ tristate "Ubicom Gigabit Ethernet support (EXPERIMENTAL)" ++ depends on UBICOM32 ++ help ++ Gigabit Ethernet support for IP5K/IP7K ++ ++config UBICOM32_NAPI ++ bool "USE NAPI (EXPERIMENTAL)" ++ depends on UBICOM32_GMAC ++ help ++ Use NAPI for Ubicom's Ethernet driver ++ ++config UBICOM32_OCM_FOR_SKB ++ bool "USE OCM for SKB (EXPERIMENTAL)" ++ depends on UBICOM32_GMAC ++ default n ++ help ++ Allocate skb from OCM for Ethernet Receive ++ + endif # NETDEV_1000 + + # +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -262,4 +262,4 @@ obj-$(CONFIG_NETXEN_NIC) += netxen/ + obj-$(CONFIG_NIU) += niu.o + obj-$(CONFIG_VIRTIO_NET) += virtio_net.o + obj-$(CONFIG_SFC) += sfc/ +- ++obj-$(CONFIG_UBICOM32_GMAC) += ubi32-eth.o +--- /dev/null ++++ b/drivers/net/ubi32-eth.c +@@ -0,0 +1,699 @@ ++/* ++ * drivers/net/ubi32-eth.c ++ * Ubicom32 ethernet TIO interface driver. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++/* ++ * ubi32_eth.c ++ * Ethernet driver for Ip5k/Ip7K ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/moduleparam.h> ++ ++#include <linux/sched.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/interrupt.h> ++ ++#include <linux/in.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/if_vlan.h> ++#include <linux/ip.h> ++#include <linux/tcp.h> ++#include <linux/skbuff.h> ++#include <asm/checksum.h> ++#include "ubi32-eth.h" ++#include <asm/ip5000.h> ++#include <asm/devtree.h> ++#include <asm/system.h> ++ ++/* #define USE_POLLING */ ++ ++/* ++ * TODO: ++ * mac address from flash ++ * multicast filter ++ * ethtool support ++ * sysfs support ++ * skb->nrfrag support ++ * ioctl ++ * monitor phy status ++ */ ++ ++static const char *eth_if_name[UBI32_ETH_NUM_OF_DEVICES] = ++ {"eth_lan", "eth_wan", "eth_ultra"}; ++static struct net_device *ubi32_eth_devices[UBI32_ETH_NUM_OF_DEVICES] = ++ {NULL, NULL, NULL}; ++static u8_t mac_addr[UBI32_ETH_NUM_OF_DEVICES][ETH_ALEN] = { ++ {0x00, 0x03, 0x64, 'l', 'a', 'n'}, ++ {0x00, 0x03, 0x64, 'w', 'a', 'n'}, ++ {0x00, 0x03, 0x64, 'u', 'l', 't'}}; ++ ++int ubi32_eth_ocm_alloc; ++int ubi32_eth_ddr_alloc; ++ ++static inline struct sk_buff *ubi32_alloc_skb(struct net_device *dev, unsigned int length) ++{ ++#if (defined(CONFIG_ZONE_DMA) && defined(CONFIG_UBICOM32_OCM_FOR_SKB)) ++ struct sk_buff *sk_buff; ++ ++ if (dev != ubi32_eth_devices[0]) ++ return __dev_alloc_skb(length, GFP_ATOMIC); ++ ++ sk_buff = __dev_alloc_skb(length, GFP_ATOMIC | __GFP_NOWARN | __GFP_NORETRY | GFP_DMA); ++ if (sk_buff) { ++ ubi32_eth_ocm_alloc++; ++ return sk_buff; ++ } else { ++ ubi32_eth_ddr_alloc++; ++ return __dev_alloc_skb(length, GFP_ATOMIC); ++ } ++#else ++ return __dev_alloc_skb(length, GFP_ATOMIC); ++#endif ++} ++ ++static void ubi32_eth_vp_rxtx_enable(struct net_device *dev) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ priv->regs->command = UBI32_ETH_VP_CMD_RX_ENABLE | UBI32_ETH_VP_CMD_TX_ENABLE; ++ priv->regs->int_mask = (UBI32_ETH_VP_INT_RX | UBI32_ETH_VP_INT_TX); ++ ubicom32_set_interrupt(priv->vp_int_bit); ++} ++ ++static void ubi32_eth_vp_rxtx_stop(struct net_device *dev) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ priv->regs->command = 0; ++ priv->regs->int_mask = 0; ++ ubicom32_set_interrupt(priv->vp_int_bit); ++ ++ /* Wait for graceful shutdown */ ++ while (priv->regs->status & (UBI32_ETH_VP_STATUS_RX_STATE | UBI32_ETH_VP_STATUS_TX_STATE)); ++} ++ ++/* ++ * ubi32_eth_tx_done() ++ */ ++static int ubi32_eth_tx_done(struct net_device *dev) ++{ ++ struct ubi32_eth_private *priv; ++ struct sk_buff *skb; ++ volatile void *pdata; ++ struct ubi32_eth_dma_desc *desc; ++ u32_t count = 0; ++ ++ priv = netdev_priv(dev); ++ ++ priv->regs->int_status &= ~UBI32_ETH_VP_INT_TX; ++ while (priv->tx_tail != priv->regs->tx_out) { ++ pdata = priv->regs->tx_dma_ring[priv->tx_tail]; ++ BUG_ON(pdata == NULL); ++ ++ skb = container_of((void *)pdata, struct sk_buff, cb); ++ desc = (struct ubi32_eth_dma_desc *)pdata; ++ if (unlikely(!(desc->status & UBI32_ETH_VP_TX_OK))) { ++ atomic_inc((atomic_t *)&priv->stats.tx_errors); ++ } else { ++ priv->stats.tx_packets++; ++ priv->stats.tx_bytes += skb->len; ++ } ++ dev_kfree_skb_any(skb); ++ priv->regs->tx_dma_ring[priv->tx_tail] = NULL; ++ priv->tx_tail = (priv->tx_tail + 1) & TX_DMA_RING_MASK; ++ count++; ++ } ++ ++ if (unlikely(priv->status & UBI32_ETH_PRIV_STATUS_TX_Q_FULL)) { ++ spin_lock(&priv->lock); ++ priv->status &= ~UBI32_ETH_PRIV_STATUS_TX_Q_FULL; ++ netif_wake_queue(dev); ++ spin_unlock(&priv->lock); ++ } ++ return count; ++} ++ ++/* ++ * ubi32_eth_receive() ++ * To avoid locking overhead, this is called only ++ * by tasklet when not using NAPI, or ++ * by NAPI poll when using NAPI. ++ * return number of frames processed ++ */ ++static int ubi32_eth_receive(struct net_device *dev, int quota) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ unsigned short rx_in = priv->regs->rx_in; ++ struct sk_buff *skb; ++ struct ubi32_eth_dma_desc *desc = NULL; ++ volatile void *pdata; ++ ++ /* The number of NET_SKB_PAD bytes are already reserved by skb */ ++ int extra_reserve = UBI32_ETH_RESERVE_SPACE - NET_SKB_PAD + UBI32_ETH_RESERVE_EXTRA; ++ int extra_alloc = extra_reserve + UBI32_ETH_TRASHED_MEMORY; ++ int i, replenish_cnt, count = 0; ++ int replenish_max = RX_DMA_MAX_QUEUE_SIZE; ++#if (defined(CONFIG_ZONE_DMA) && defined(CONFIG_UBICOM32_OCM_FOR_SKB)) ++ if (dev == ubi32_eth_devices[0]) ++ replenish_max = 21; ++#endif ++ BUG_ON((extra_reserve & 3) != NET_IP_ALIGN); /* Must provide NET_IP_ALIGN (0 or 2) */ ++ ++ if (unlikely(rx_in == priv->regs->rx_out)) ++ priv->vp_stats.rx_q_full_cnt++; ++ ++ priv->regs->int_status &= ~UBI32_ETH_VP_INT_RX; ++ while (priv->rx_tail != priv->regs->rx_out) { ++ if (unlikely(count == quota)) { ++ /* There is still frame pending to be processed */ ++ priv->vp_stats.rx_throttle++; ++ break; ++ } ++ ++ pdata = priv->regs->rx_dma_ring[priv->rx_tail]; ++ BUG_ON(pdata == NULL); ++ ++ desc = (struct ubi32_eth_dma_desc *)pdata; ++ skb = container_of((void *)pdata, struct sk_buff, cb); ++ count++; ++ priv->regs->rx_dma_ring[priv->rx_tail] = NULL; ++ priv->rx_tail = ((priv->rx_tail + 1) & RX_DMA_RING_MASK); ++ ++ /* ++ * Check only RX_OK bit here. ++ * The rest of status word is used as timestamp ++ */ ++ if (unlikely(!(desc->status & UBI32_ETH_VP_RX_OK))) { ++ priv->stats.rx_errors++; ++ dev_kfree_skb_any(skb); ++ continue; ++ } ++ ++ skb_put(skb, desc->data_len); ++ skb->dev = dev; ++ skb->protocol = eth_type_trans(skb, dev); ++ skb->ip_summed = CHECKSUM_NONE; ++ priv->stats.rx_bytes += skb->len; ++ priv->stats.rx_packets++; ++#ifndef CONFIG_UBICOM32_NAPI ++ netif_rx(skb); ++#else ++ netif_receive_skb(skb); ++#endif ++ } ++ ++ /* fill in more descripor for VP*/ ++ replenish_cnt = replenish_max - ++ ((RX_DMA_RING_SIZE + rx_in - priv->rx_tail) & RX_DMA_RING_MASK); ++ if (replenish_cnt > 0) { ++ for (i = 0; i < replenish_cnt; i++) { ++ skb = ubi32_alloc_skb(dev, RX_BUF_SIZE + extra_alloc); ++ if (!skb) { ++ priv->vp_stats.rx_alloc_err++; ++ break; ++ } ++ /* set up dma descriptor */ ++ desc = (struct ubi32_eth_dma_desc *)skb->cb; ++ skb_reserve(skb, extra_reserve); ++ BUG_ON((((u32)skb->data + ETH_HLEN) & (CACHE_LINE_SIZE - 1)) != 0); ++ desc->data_pointer = skb->data; ++ desc->buffer_len = RX_BUF_SIZE + UBI32_ETH_TRASHED_MEMORY; ++ desc->data_len = 0; ++ desc->status = 0; ++ priv->regs->rx_dma_ring[rx_in] = desc; ++ rx_in = (rx_in + 1) & RX_DMA_RING_MASK; ++ } ++ wmb(); ++ priv->regs->rx_in = rx_in; ++ ubicom32_set_interrupt(priv->vp_int_bit); ++ } ++ ++ if (likely(count > 0)) { ++ dev->last_rx = jiffies; ++ } ++ return count; ++} ++ ++#ifdef CONFIG_UBICOM32_NAPI ++static int ubi32_eth_napi_poll(struct napi_struct *napi, int budget) ++{ ++ struct ubi32_eth_private *priv = container_of(napi, struct ubi32_eth_private, napi); ++ struct net_device *dev = priv->dev; ++ u32_t count; ++ ++ if (priv->tx_tail != priv->regs->tx_out) { ++ ubi32_eth_tx_done(dev); ++ } ++ ++ count = ubi32_eth_receive(dev, budget); ++ ++ priv->regs->int_mask |= (UBI32_ETH_VP_INT_RX | UBI32_ETH_VP_INT_TX); ++ if (count < budget) { ++ netif_rx_complete(dev, napi); ++ if ((priv->rx_tail != priv->regs->rx_out) || (priv->tx_tail != priv->regs->tx_out)) { ++ if (netif_rx_reschedule(dev, napi)) { ++ priv->regs->int_mask = 0; ++ } ++ } ++ } ++ return count; ++} ++ ++#else ++static void ubi32_eth_do_tasklet(unsigned long arg) ++{ ++ struct net_device *dev = (struct net_device *)arg; ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ ++ if (priv->tx_tail != priv->regs->tx_out) { ++ ubi32_eth_tx_done(dev); ++ } ++ ++ /* always call receive to process new RX frame as well as replenish RX buffers */ ++ ubi32_eth_receive(dev, RX_BOUND); ++ ++ priv->regs->int_mask |= (UBI32_ETH_VP_INT_RX | UBI32_ETH_VP_INT_TX); ++ if ((priv->rx_tail != priv->regs->rx_out) || (priv->tx_tail != priv->regs->tx_out)) { ++ priv->regs->int_mask = 0; ++ tasklet_schedule(&priv->tsk); ++ } ++} ++#endif ++ ++#if defined(USE_POLLING) ++static struct timer_list eth_poll_timer; ++ ++static void ubi32_eth_poll(unsigned long arg) ++{ ++ struct net_device *dev; ++ struct ubi32_eth_private *priv; ++ int i; ++ ++ for (i = 0; i < UBI32_ETH_NUM_OF_DEVICES; i++) { ++ dev = ubi32_eth_devices[i]; ++ if (dev && (dev->flags & IFF_UP)) { ++ priv = netdev_priv(dev); ++#ifdef CONFIG_UBICOM32_NAPI ++ netif_rx_schedule(dev, &priv->napi); ++#else ++ tasklet_schedule(&priv->tsk); ++#endif ++ } ++ } ++ ++ eth_poll_timer.expires = jiffies + 2; ++ add_timer(ð_poll_timer); ++} ++ ++#else ++static irqreturn_t ubi32_eth_interrupt(int irq, void *dev_id) ++{ ++ struct ubi32_eth_private *priv; ++ ++ struct net_device *dev = (struct net_device *)dev_id; ++ BUG_ON(irq != dev->irq); ++ ++ priv = netdev_priv(dev); ++ if (unlikely(!(priv->regs->int_status & priv->regs->int_mask))) { ++ return IRQ_NONE; ++ } ++ ++ /* ++ * Disable port interrupt ++ */ ++#ifdef CONFIG_UBICOM32_NAPI ++ if (netif_rx_schedule_prep(dev, &priv->napi)) { ++ priv->regs->int_mask = 0; ++ __netif_rx_schedule(dev, &priv->napi); ++ } ++#else ++ priv->regs->int_mask = 0; ++ tasklet_schedule(&priv->tsk); ++#endif ++ return IRQ_HANDLED; ++} ++#endif ++ ++/* ++ * ubi32_eth_open ++ */ ++static int ubi32_eth_open(struct net_device *dev) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ int err; ++ ++ printk(KERN_INFO "eth open %s\n",dev->name); ++#ifndef USE_POLLING ++ /* request_region() */ ++ err = request_irq(dev->irq, ubi32_eth_interrupt, IRQF_DISABLED, dev->name, dev); ++ if (err) { ++ printk(KERN_WARNING "fail to request_irq %d\n",err); ++ return -ENODEV; ++ } ++#endif ++#ifdef CONFIG_UBICOM32_NAPI ++ napi_enable(&priv->napi); ++#else ++ tasklet_init(&priv->tsk, ubi32_eth_do_tasklet, (unsigned long)dev); ++#endif ++ ++ /* call receive to supply RX buffers */ ++ ubi32_eth_receive(dev, RX_DMA_MAX_QUEUE_SIZE); ++ ++ /* check phy status and call netif_carrier_on */ ++ ubi32_eth_vp_rxtx_enable(dev); ++ netif_start_queue(dev); ++ return 0; ++} ++ ++static int ubi32_eth_close(struct net_device *dev) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ volatile void *pdata; ++ struct sk_buff *skb; ++ ++#ifndef USE_POLLING ++ free_irq(dev->irq, dev); ++#endif ++ netif_stop_queue(dev); /* can't transmit any more */ ++#ifdef CONFIG_UBICOM32_NAPI ++ napi_disable(&priv->napi); ++#else ++ tasklet_kill(&priv->tsk); ++#endif ++ ubi32_eth_vp_rxtx_stop(dev); ++ ++ /* ++ * RX clean up ++ */ ++ while (priv->rx_tail != priv->regs->rx_in) { ++ pdata = priv->regs->rx_dma_ring[priv->rx_tail]; ++ skb = container_of((void *)pdata, struct sk_buff, cb); ++ priv->regs->rx_dma_ring[priv->rx_tail] = NULL; ++ dev_kfree_skb_any(skb); ++ priv->rx_tail = ((priv->rx_tail + 1) & RX_DMA_RING_MASK); ++ } ++ priv->regs->rx_in = 0; ++ priv->regs->rx_out = priv->regs->rx_in; ++ priv->rx_tail = priv->regs->rx_in; ++ ++ /* ++ * TX clean up ++ */ ++ BUG_ON(priv->regs->tx_out != priv->regs->tx_in); ++ ubi32_eth_tx_done(dev); ++ BUG_ON(priv->tx_tail != priv->regs->tx_in); ++ priv->regs->tx_in = 0; ++ priv->regs->tx_out = priv->regs->tx_in; ++ priv->tx_tail = priv->regs->tx_in; ++ ++ return 0; ++} ++ ++/* ++ * ubi32_eth_set_config ++ */ ++static int ubi32_eth_set_config(struct net_device *dev, struct ifmap *map) ++{ ++ /* if must to down to config it */ ++ printk(KERN_INFO "set_config %x\n", dev->flags); ++ if (dev->flags & IFF_UP) ++ return -EBUSY; ++ ++ /* I/O and IRQ can not be changed */ ++ if (map->base_addr != dev->base_addr) { ++ printk(KERN_WARNING "%s: Can't change I/O address\n", dev->name); ++ return -EOPNOTSUPP; ++ } ++ ++#ifndef USE_POLLING ++ if (map->irq != dev->irq) { ++ printk(KERN_WARNING "%s: Can't change IRQ\n", dev->name); ++ return -EOPNOTSUPP; ++ } ++#endif ++ ++ /* ignore other fields */ ++ return 0; ++} ++ ++static int ubi32_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ struct ubi32_eth_dma_desc *desc = NULL; ++ unsigned short space, tx_in; ++ ++ tx_in = priv->regs->tx_in; ++ ++ dev->trans_start = jiffies; /* save the timestamp */ ++ space = TX_DMA_RING_MASK - ((TX_DMA_RING_SIZE + tx_in - priv->tx_tail) & TX_DMA_RING_MASK); ++ ++ if (unlikely(space == 0)) { ++ atomic_inc((atomic_t *)&priv->stats.tx_errors); ++ if (!(priv->status & UBI32_ETH_PRIV_STATUS_TX_Q_FULL)) { ++ spin_lock(&priv->lock); ++ priv->status |= UBI32_ETH_PRIV_STATUS_TX_Q_FULL; ++ priv->vp_stats.tx_q_full_cnt++; ++ netif_stop_queue(dev); ++ spin_unlock(&priv->lock); ++ } ++ ++ /* give both HW and this driver an extra trigger */ ++ priv->regs->int_mask |= UBI32_ETH_VP_INT_TX; ++#ifndef USE_POLLING ++ ubicom32_set_interrupt(dev->irq); ++#endif ++ ubicom32_set_interrupt(priv->vp_int_bit); ++ ++ return NETDEV_TX_BUSY; ++ } ++ ++ /*still have room */ ++ desc = (struct ubi32_eth_dma_desc *)skb->cb; ++ desc->data_pointer = skb->data; ++ desc->data_len = skb->len; ++ priv->regs->tx_dma_ring[tx_in] = desc; ++ tx_in = ((tx_in + 1) & TX_DMA_RING_MASK); ++ wmb(); ++ priv->regs->tx_in = tx_in; ++ /* kick the HRT */ ++ ubicom32_set_interrupt(priv->vp_int_bit); ++ ++ return 0; ++} ++ ++/* ++ * Deal with a transmit timeout. ++ */ ++static void ubi32_eth_tx_timeout (struct net_device *dev) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ atomic_inc((atomic_t *)&priv->stats.tx_errors); ++ priv->regs->int_mask |= UBI32_ETH_VP_INT_TX; ++#ifndef USE_POLLING ++ ubicom32_set_interrupt(dev->irq); ++#endif ++ ubicom32_set_interrupt(priv->vp_int_bit); ++} ++ ++static int ubi32_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ printk(KERN_INFO "ioctl %s, %d\n", dev->name, cmd); ++ return -EINVAL; ++} ++ ++/* ++ * Return statistics to the caller ++ */ ++static struct net_device_stats *ubi32_eth_get_stats(struct net_device *dev) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ return &priv->stats; ++} ++ ++ ++static int ubi32_eth_change_mtu(struct net_device *dev, int new_mtu) ++{ ++ struct ubi32_eth_private *priv = netdev_priv(dev); ++ unsigned long flags; ++ ++ if ((new_mtu < 68) || (new_mtu > 1500)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ dev->mtu = new_mtu; ++ spin_unlock_irqrestore(&priv->lock, flags); ++ printk(KERN_INFO "set mtu to %d", new_mtu); ++ return 0; ++} ++ ++/* ++ * ubi32_eth_cleanup: unload the module ++ */ ++void ubi32_eth_cleanup(void) ++{ ++ struct ubi32_eth_private *priv; ++ struct net_device *dev; ++ int i; ++ ++ for (i = 0; i < UBI32_ETH_NUM_OF_DEVICES; i++) { ++ dev = ubi32_eth_devices[i]; ++ if (dev) { ++ priv = netdev_priv(dev); ++ kfree(priv->regs->tx_dma_ring); ++ unregister_netdev(dev); ++ free_netdev(dev); ++ ubi32_eth_devices[i] = NULL; ++ } ++ } ++} ++ ++int ubi32_eth_init_module(void) ++{ ++ struct ethtionode *eth_node; ++ struct net_device *dev; ++ struct ubi32_eth_private *priv; ++ int i, err; ++ ++ /* ++ * Device allocation. ++ */ ++ err = 0; ++ for (i = 0; i < UBI32_ETH_NUM_OF_DEVICES; i++) { ++ /* ++ * See if the eth_vp is in the device tree. ++ */ ++ eth_node = (struct ethtionode *)devtree_find_node(eth_if_name[i]); ++ if (!eth_node) { ++ printk(KERN_INFO "%s does not exist\n", eth_if_name[i]); ++ continue; ++ } ++ ++ eth_node->tx_dma_ring = (struct ubi32_eth_dma_desc **)kmalloc( ++ sizeof(struct ubi32_eth_dma_desc *) * ++ (TX_DMA_RING_SIZE + RX_DMA_RING_SIZE), GFP_ATOMIC | __GFP_NOWARN | __GFP_NORETRY | GFP_DMA); ++ ++ if (eth_node->tx_dma_ring == NULL) { ++ eth_node->tx_dma_ring = (struct ubi32_eth_dma_desc **)kmalloc( ++ sizeof(struct ubi32_eth_dma_desc *) * ++ (TX_DMA_RING_SIZE + RX_DMA_RING_SIZE), GFP_KERNEL); ++ printk(KERN_INFO "Failed to allocate from OCM\n"); ++ } ++ ++ if (!eth_node->tx_dma_ring) { ++ err = -ENOMEM; ++ break; ++ } ++ eth_node->rx_dma_ring = eth_node->tx_dma_ring + TX_DMA_RING_SIZE; ++ eth_node->tx_sz = TX_DMA_RING_SIZE - 1; ++ eth_node->rx_sz = RX_DMA_RING_SIZE - 1; ++ ++ dev = alloc_etherdev(sizeof(struct ubi32_eth_private)); ++ if (!dev) { ++ kfree(eth_node->tx_dma_ring); ++ err = -ENOMEM; ++ break; ++ } ++ priv = netdev_priv(dev); ++ priv->dev = dev; ++ ++ /* ++ * FIX IT! get proper mac address ++ */ ++ memcpy(dev->dev_addr, mac_addr[i], ETH_ALEN); ++ ++ priv->regs = eth_node; ++ priv->regs->command = 0; ++ priv->regs->int_mask = 0; ++ priv->regs->int_status = 0; ++ priv->regs->tx_out = 0; ++ priv->regs->rx_out = 0; ++ priv->regs->tx_in = 0; ++ priv->regs->rx_in = 0; ++ priv->rx_tail = 0; ++ priv->tx_tail = 0; ++ ++ priv->vp_int_bit = eth_node->dn.sendirq; ++ dev->irq = eth_node->dn.recvirq; ++ ++ spin_lock_init(&priv->lock); ++ ++ dev->open = ubi32_eth_open; ++ dev->stop = ubi32_eth_close; ++ dev->hard_start_xmit = ubi32_eth_start_xmit; ++ dev->tx_timeout = ubi32_eth_tx_timeout; ++ dev->watchdog_timeo = UBI32_ETH_VP_TX_TIMEOUT; ++ ++ dev->set_config = ubi32_eth_set_config; ++ dev->do_ioctl = ubi32_eth_ioctl; ++ dev->get_stats = ubi32_eth_get_stats; ++ dev->change_mtu = ubi32_eth_change_mtu; ++#ifdef CONFIG_UBICOM32_NAPI ++ netif_napi_add(dev, &priv->napi, ubi32_eth_napi_poll, UBI32_ETH_NAPI_WEIGHT); ++#endif ++ err = register_netdev(dev); ++ if (err) { ++ printk(KERN_WARNING "Failed to register netdev %s\n", eth_if_name[i]); ++ //release_region(); ++ free_netdev(dev); ++ kfree(eth_node->tx_dma_ring); ++ break; ++ } ++ ++ ubi32_eth_devices[i] = dev; ++ printk(KERN_INFO "%s vp_base:0x%p, tio_int:%d irq:%d\n", ++ dev->name, priv->regs, eth_node->dn.sendirq, dev->irq); ++ } ++ ++ if (err) { ++ ubi32_eth_cleanup(); ++ return err; ++ } ++ ++ if (!ubi32_eth_devices[0] && !ubi32_eth_devices[1] && !ubi32_eth_devices[2]) { ++ return -ENODEV; ++ } ++ ++#if defined(USE_POLLING) ++ init_timer(ð_poll_timer); ++ eth_poll_timer.function = ubi32_eth_poll; ++ eth_poll_timer.data = (unsigned long)0; ++ eth_poll_timer.expires = jiffies + 2; ++ add_timer(ð_poll_timer); ++#endif ++ ++ return 0; ++} ++ ++module_init(ubi32_eth_init_module); ++module_exit(ubi32_eth_cleanup); ++ ++MODULE_AUTHOR("Kan Yan, Greg Ren"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/net/ubi32-eth.h +@@ -0,0 +1,133 @@ ++/* ++ * drivers/net/ubi32-eth.h ++ * Ubicom32 ethernet TIO interface driver definitions. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#ifndef _UBI32_ETH_H ++#define _UBI32_ETH_H ++ ++#include <asm/devtree.h> ++ ++#define UBI32_ETH_NUM_OF_DEVICES 3 ++ ++/* ++ * Number of bytes trashed beyond the packet data. ++ */ ++#define UBI32_ETH_TRASHED_MEMORY (CACHE_LINE_SIZE + ETH_HLEN - 1) ++ ++/* ++ * Linux already reserves NET_SKB_PAD bytes of headroom in each sk_buff. ++ * We want to be able to reserve arbitrarily more space to align Ethernet ++ * and IP header to cache line. ++ * Note that the TIO expects a CACHE_LINE_SIZE - ETH_HLEN aligned Ethernet ++ * header, so the reserved space must be NET_SKB_PAD + NET_IP_ALIGN. ++ * (NET_SKB_PAD is 16, NET_IP_ALIGN is 2, CACHE_LINE_SIZE is 32). ++ * You can add more space by making UBI32_ETH_iRESERVE_EXTRA != 0. ++ */ ++#define UBI32_ETH_RESERVE_SPACE (CACHE_LINE_SIZE - ETH_HLEN) ++#define UBI32_ETH_RESERVE_EXTRA (1 * CACHE_LINE_SIZE) ++ ++struct ubi32_eth_dma_desc { ++ volatile void *data_pointer; /* pointer to the buffer */ ++ volatile u16 buffer_len; /* the buffer size */ ++ volatile u16 data_len; /* actual frame length */ ++ volatile u32 status; /* bit0: status to be update by VP; bit[31:1] time stamp */ ++}; ++ ++#define TX_DMA_RING_SIZE (1<<8) ++#define TX_DMA_RING_MASK (TX_DMA_RING_SIZE - 1) ++#define RX_DMA_RING_SIZE (1<<8) ++#define RX_DMA_RING_MASK (RX_DMA_RING_SIZE - 1) ++ ++#define RX_DMA_MAX_QUEUE_SIZE (RX_DMA_RING_SIZE - 1) /* no more than (RX_DMA_RING_SIZE - 1) */ ++#define RX_MAX_PKT_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN) ++#define RX_MIN_PKT_SIZE ETH_ZLEN ++#define RX_BUF_SIZE (RX_MAX_PKT_SIZE + VLAN_HLEN) /* allow double VLAN tag */ ++ ++#define UBI32_ETH_VP_TX_TIMEOUT (10*HZ) ++ ++struct ubi32_eth_vp_stats { ++ u32 rx_alloc_err; ++ u32 tx_q_full_cnt; ++ u32 rx_q_full_cnt; ++ u32 rx_throttle; ++}; ++ ++struct ubi32_eth_private { ++ struct net_device *dev; ++ struct net_device_stats stats; ++ struct ubi32_eth_vp_stats vp_stats; ++ int status; ++ spinlock_t lock; ++#ifdef CONFIG_UBICOM32_NAPI ++ struct napi_struct napi; ++#else ++ struct tasklet_struct tsk; ++#endif ++ struct ethtionode *regs; ++ u16 rx_tail; ++ u16 tx_tail; ++ u32 vp_int_bit; ++}; ++ ++struct ethtionode { ++ struct devtree_node dn; ++ volatile u16 command; ++ volatile u16 status; ++ volatile u16 int_mask; /* interrupt mask */ ++ volatile u16 int_status; /* interrupt mask */ ++ volatile u16 tx_in; /* owned by driver */ ++ volatile u16 tx_out; /* owned by vp */ ++ volatile u16 rx_in; /* owned by driver */ ++ volatile u16 rx_out; /* owned by vp */ ++ u16 tx_sz; /* owned by driver */ ++ u16 rx_sz; /* owned by driver */ ++ struct ubi32_eth_dma_desc **tx_dma_ring; ++ struct ubi32_eth_dma_desc **rx_dma_ring; ++}; ++ ++#define UBI32_ETH_VP_STATUS_LINK (1<<0) ++#define UBI32_ETH_VP_STATUS_SPEED (0x2<<1) ++#define UBI32_ETH_VP_STATUS_DUPLEX (0x1<<3) ++#define UBI32_ETH_VP_STATUS_FLOW_CTRL (0x1<<4) ++ ++#define UBI32_ETH_VP_STATUS_RX_STATE (0x1<<5) ++#define UBI32_ETH_VP_STATUS_TX_STATE (0x1<<6) ++ ++#define UBI32_ETH_PRIV_STATUS_TX_Q_FULL (1<<0) ++ ++#define UBI32_ETH_VP_INT_RX (1<<0) ++#define UBI32_ETH_VP_INT_TX (1<<1) ++ ++#define UBI32_ETH_VP_CMD_RX_ENABLE (1<<0) ++#define UBI32_ETH_VP_CMD_TX_ENABLE (1<<1) ++ ++#define UBI32_ETH_VP_RX_OK (1<<0) ++#define UBI32_ETH_VP_TX_OK (1<<1) ++ ++#define TX_BOUND TX_DMA_RING_SIZE ++#define RX_BOUND 64 ++#define UBI32_ETH_NAPI_WEIGHT 64 /* for GigE */ ++#endif +--- a/drivers/net/usb/asix.c ++++ b/drivers/net/usb/asix.c +@@ -318,14 +318,33 @@ static int asix_rx_fixup(struct usbnet * + /* get the packet length */ + size = (u16) (header & 0x0000ffff); + +- if ((skb->len) - ((size + 1) & 0xfffe) == 0) ++ if ((skb->len) - ((size + 1) & 0xfffe) == 0) { ++#ifndef HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (((u32)packet & 0x02) == 0) { ++ memmove(packet - 2, packet, size); ++ skb->data -= 2; ++ skb->tail -= 2; ++ } ++#endif + return 2; ++ } ++ + if (size > ETH_FRAME_LEN) { + deverr(dev,"asix_rx_fixup() Bad RX Length %d", size); + return 0; + } + ax_skb = skb_clone(skb, GFP_ATOMIC); + if (ax_skb) { ++#ifndef HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (((u32)packet & 0x02) == 0) { ++ memmove(packet - 2, packet, size); ++ ax_skb->data = packet - 2; ++ } else { ++ ax_skb->data = packet; ++ } ++#else ++ ax_skb->data = packet; ++#endif + ax_skb->len = size; + ax_skb->data = packet; + skb_set_tail_pointer(ax_skb, size); +@@ -1102,13 +1121,19 @@ static int ax88178_link_reset(struct usb + mode = AX88178_MEDIUM_DEFAULT; + + if (ecmd.speed == SPEED_1000) ++#ifdef HAVE_EFFICIENT_UNALIGNED_ACCESS + mode |= AX_MEDIUM_GM; ++#else ++ mode |= AX_MEDIUM_GM | AX_MEDIUM_ENCK; ++#endif + else if (ecmd.speed == SPEED_100) + mode |= AX_MEDIUM_PS; + else + mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM); + ++#ifdef HAVE_EFFICIENT_UNALIGNED_ACCESS + mode |= AX_MEDIUM_ENCK; ++#endif + + if (ecmd.duplex == DUPLEX_FULL) + mode |= AX_MEDIUM_FD; +--- a/drivers/oprofile/cpu_buffer.c ++++ b/drivers/oprofile/cpu_buffer.c +@@ -240,10 +240,11 @@ static void oprofile_end_trace(struct op + cpu_buf->tracing = 0; + } + +-void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, +- unsigned long event, int is_kernel) ++ ++void oprofile_add_ext_sample_cpu(unsigned long pc, struct pt_regs * const regs, ++ unsigned long event, int is_kernel, int cpu) + { +- struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); ++ struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); + + if (!backtrace_depth) { + log_sample(cpu_buf, pc, is_kernel, event); +@@ -260,6 +261,13 @@ void oprofile_add_ext_sample(unsigned lo + oprofile_end_trace(cpu_buf); + } + ++void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, ++ unsigned long event, int is_kernel) ++{ ++ oprofile_add_ext_sample_cpu(pc, regs, event, ++ is_kernel, smp_processor_id()); ++} ++ + void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) + { + int is_kernel = !user_mode(regs); +--- a/drivers/pci/Makefile ++++ b/drivers/pci/Makefile +@@ -42,8 +42,8 @@ obj-$(CONFIG_PPC) += setup-bus.o + obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o + obj-$(CONFIG_X86_VISWS) += setup-irq.o + obj-$(CONFIG_MN10300) += setup-bus.o ++obj-$(CONFIG_UBICOM32) += setup-bus.o setup-irq.o + +-# + # ACPI Related PCI FW Functions + # + obj-$(CONFIG_ACPI) += pci-acpi.o +--- a/drivers/serial/Kconfig ++++ b/drivers/serial/Kconfig +@@ -827,6 +827,57 @@ config SERIAL_UARTLITE_CONSOLE + console (the system console is the device which receives all kernel + messages and warnings and which allows logins in single user mode). + ++config SERIAL_UBI32_UARTTIO ++ tristate "Ubicom UARTTIO support" ++ depends on UBICOM32=y ++ select SERIAL_CORE ++ default y ++ help ++ Add support for the Ubicom virtual peripherial serial interface. ++ ++config SERIAL_UBI32_UARTTIO_NR_UARTS ++ int "Maximum number of UARTTIO virtual serial ports" ++ depends on SERIAL_UBI32_UARTTIO ++ default "4" ++ help ++ Set this to the maximum number of serial ports you want the driver to support. ++ ++config SERIAL_UBI32_UARTTIO_CONSOLE ++ tristate "Ubicom UARTTIO console support" ++ depends on SERIAL_UBI32_UARTTIO=y ++ select SERIAL_CORE_CONSOLE ++ default y ++ help ++ Add support for console on the Ubicom virtual peripherial serial interface. ++ ++config SERIAL_UBI32_SERDES ++ bool "Ubicom serial port support" ++ depends on UBICOM32=y ++ select SERIAL_CORE ++ default y ++ help ++ Add support for the Ubicom serial interface. ++ ++config SERIAL_UBI32_SERDES_CONSOLE ++ bool "Ubicom serial console support" ++ depends on SERIAL_UBI32_SERDES=y ++ select SERIAL_CORE_CONSOLE ++ default y ++ ++config SERIAL_UBI32_MAILBOX ++ bool "Ubicom mailbox support" ++ depends on UBICOM32=y ++ select SERIAL_CORE ++ default n ++ help ++ Add support for the Ubicom mailbox interface. ++ ++config SERIAL_UBI32_MAILBOX_CONSOLE ++ bool "Ubicom mailbox console support" ++ depends on SERIAL_UBI32_MAILBOX=y ++ select SERIAL_CORE_CONSOLE ++ default y ++ + config SERIAL_SUNCORE + bool + depends on SPARC +--- a/drivers/serial/Makefile ++++ b/drivers/serial/Makefile +@@ -73,3 +73,6 @@ obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_s + obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o + obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o + obj-$(CONFIG_SERIAL_QE) += ucc_uart.o ++obj-$(CONFIG_SERIAL_UBI32_SERDES) += ubi32_serdes.o ++obj-$(CONFIG_SERIAL_UBI32_UARTTIO) += ubi32_uarttio.o ++obj-$(CONFIG_SERIAL_UBI32_MAILBOX) += ubi32_mailbox.o +--- /dev/null ++++ b/drivers/serial/ubi32_mailbox.c +@@ -0,0 +1,928 @@ ++/* ++ * drivers/serial/ubi32_mailbox.c ++ * Ubicom32 On-Chip Mailbox Driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/ioport.h> ++#include <linux/init.h> ++#include <linux/console.h> ++#include <linux/sysrq.h> ++#include <linux/platform_device.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++#include <linux/serial_core.h> ++ ++#include <asm/ip5000.h> ++ ++#define SERIAL_UBICOM_BAUDRATE 115200 ++#define SERIAL_UBICOM_DATA_BIT 8 /* Fixed parameter - do not change */ ++#define SERIAL_UBICOM_PAR_BIT 0 /* Fixed parameter - do not change */ ++#define SERIAL_UBICOM_STOP_BIT 1 /* Fixed parameter - do not change */ ++ ++/* UART name and device definitions */ ++#define UBI32_MAILBOX_NAME "ttyUM" // XXX ++#define UBI32_MAILBOX_MAJOR 207 // XXX ++#define UBI32_MAILBOX_MINOR 64 ++ ++#define PORT_UBI32_MAILBOX 1235 ++#define NR_PORTS 1 ++ ++#define get_sclk() 0 ++ ++struct ubi32_mailbox_port { ++ struct uart_port port; ++ /* ++ * NOTE (rkeller): ++ * the uart port is wrapped in another structure in case we need to hold more state than ++ * what we can hold in the uart_port. ++ * Not sure if we need this, I took over the concept from the blackfin driver. ++ */ ++} ubi32_mailbox_ports[NR_PORTS]; ++ ++struct ubi32_mailbox_resource { ++ int uart_base_addr; ++ int uart_irq; ++} ubi32_mailbox_resource[NR_PORTS] = { ++ /* ++ * uart_base_addr has to be non-NULL because it is put in the uart_port membase. ++ * If membase if null the kernel skips the configuration and our port_type never gets set. ++ */ ++ {ISD_MAILBOX_BASE, ISD_MAILBOX_INT} ++}; ++ ++static volatile struct ubicom32_isd_mailbox { ++ volatile u32_t in; ++ volatile u32_t out; ++ volatile u32_t status; ++} *ubi32_mailbox = (struct ubicom32_isd_mailbox *)ISD_MAILBOX_BASE; ++ ++static void ubi32_mailbox_tx_chars(struct ubi32_mailbox_port *uart); ++ ++static void ubi32_mailbox_mctrl_check(struct ubi32_mailbox_port *uart); ++ ++#define TRUE 1 ++#define FALSE 0 ++ ++static int mailbox_console_flg = TRUE; ++static int num_timeouts = 0; ++ ++/* ++ * dummy functions and defined to be able to compile the Blackfin code ++ */ ++#define UART_GET_LSR(port) (1) ++#define UART_PUT_LSR(port, bits) ++#define UART_CLEAR_LSR(port) (1) ++#define TEMT 1 ++#define TFI 1 ++#define BI 1 ++#define PE 1 ++#define OE 1 ++#define FE 1 ++#define THRE 1 ++#define DR 1 ++#define UART_GET_LCR(port) (1) ++#define UART_PUT_LCR(port, bits) ++#define SB 1 ++#define STB 1 ++#define PEN 1 ++#define EPS 1 ++#define STP 1 ++#define WLS(n) 0 ++#define UART_GET_IER(port) (1) ++#define UART_SET_IER(port, bits) ++#define UART_CLEAR_IER(port, bits) ++#define ETBEI 0 ++#define ERBFI 0 ++#define UART_GET_CHAR(port) ubi32_mailbox_get_char() ++#define UART_PUT_CHAR(port, ch) ubi32_mailbox_put_char(ch) ++#define SSYNC() ++#define UART_GET_DLL(port) 0 ++#define UART_PUT_DLL(port, ch) ++#define UART_GET_DLH(port) 0 ++#define UART_PUT_DLH(port, ch) ++#define UART_GET_GCTL(port) (0) ++#define UART_PUT_GCTL(port, ch) ++#define UCEN 1 ++ ++/* ++ * ubi32_mailbox_get_char_avail() ++ */ ++static int ubi32_mailbox_get_char_avail(void) ++{ ++ return !(ubi32_mailbox->status & ISD_MAILBOX_STATUS_IN_EMPTY); ++} ++ ++/* ++ * ubi32_mailbox_get_char() ++ */ ++static u32_t ubi32_mailbox_get_char(void) ++{ ++ if (mailbox_console_flg == TRUE) { ++ /* ++ * Mailbox console is connected. ++ */ ++ while (ubi32_mailbox->status & ISD_MAILBOX_STATUS_IN_EMPTY); ++ return ubi32_mailbox->in & 0xff; ++ } ++ ++ /* ++ * Mailbox console was not connected. ++ */ ++ if (ubi32_mailbox->status & ISD_MAILBOX_STATUS_IN_EMPTY) { ++ return 0xff; ++ } ++ ++ /* ++ * Mailbox console is connecting. ++ */ ++ mailbox_console_flg = TRUE; ++ num_timeouts = 0; ++ return ubi32_mailbox->in & 0xff; ++} ++ ++#define MAILBOX_MAX_ATTEMPTS 1000000 ++#define MAILBOX_MAX_TIMEOUTS 5 ++/* ++ * ubi32_mailbox_put_char() ++ */ ++static void ubi32_mailbox_put_char(u32_t v) ++{ ++ /* ++ * Wait to be able to output. ++ */ ++ u32_t num_attempts = 0; ++ ++ if(mailbox_console_flg == TRUE) { ++ while(num_attempts++ < MAILBOX_MAX_ATTEMPTS) { ++ if(ubi32_mailbox->status & ISD_MAILBOX_STATUS_OUT_EMPTY) { ++ break; ++ } ++ } ++ ++ /* ++ * If timed out more than 5 times on send, mailbox console is disconnected now. ++ */ ++ if (num_attempts > MAILBOX_MAX_ATTEMPTS) { ++ if (num_timeouts++ > MAILBOX_MAX_TIMEOUTS) { ++ mailbox_console_flg = FALSE; ++ } ++ } ++ } ++ ++ asm volatile( ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ "pipe_flush 0 \n\t" ++ ); ++ ++ ubi32_mailbox->out = v & 0xff; ++} ++ ++static void ubi32_mailbox_hw_init(struct ubi32_mailbox_port *uart) ++{ ++// NOTE: It does not do any good to do these here because we are running on the linux hardware thread, ++// and these have to be called on the ldsr thread. ++// ubicom32_clear_interrupt(ISD_MAILBOX_INT); ++// ubicom32_enable_interrupt(ISD_MAILBOX_INT); ++} ++ ++/* ++ * interrupts are disabled on entry ++ */ ++static void ubi32_mailbox_stop_tx(struct uart_port *port) ++{ ++// struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++// struct circ_buf *xmit = &uart->port.info->xmit; ++ ++ while (!(UART_GET_LSR(uart) & TEMT)) ++ cpu_relax(); ++ ++ /* Clear TFI bit */ ++ UART_PUT_LSR(uart, TFI); ++ UART_CLEAR_IER(uart, ETBEI); ++} ++ ++/* ++ * port is locked and interrupts are disabled ++ */ ++static void ubi32_mailbox_start_tx(struct uart_port *port) ++{ ++ struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ ++ UART_SET_IER(uart, ETBEI); ++ ++ ubi32_mailbox_tx_chars(uart); ++} ++ ++/* ++ * Interrupts are enabled ++ */ ++static void ubi32_mailbox_stop_rx(struct uart_port *port) ++{ ++// struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ UART_CLEAR_IER(uart, ERBFI); ++} ++ ++/* ++ * Set the modem control timer to fire immediately. ++ */ ++static void ubi32_mailbox_enable_ms(struct uart_port *port) ++{ ++} ++ ++static void ubi32_mailbox_rx_chars(struct ubi32_mailbox_port *uart) ++{ ++ struct uart_info *info = uart->port.info; ++ struct tty_struct *tty = info->port.tty; ++ unsigned int status, ch, flg; ++ ++ status = 0; // XXX? UART_GET_LSR(uart); ++ UART_CLEAR_LSR(uart); ++ ++ ch = UART_GET_CHAR(uart); ++ ++ if(ch == 0xff) ++ return; ++ ++ uart->port.icount.rx++; ++ ++ if (status & BI) { ++ uart->port.icount.brk++; ++ if (uart_handle_break(&uart->port)) ++ goto ignore_char; ++ status &= ~(PE | FE); ++ } ++ if (status & PE) ++ uart->port.icount.parity++; ++ if (status & OE) ++ uart->port.icount.overrun++; ++ if (status & FE) ++ uart->port.icount.frame++; ++ ++ status &= uart->port.read_status_mask; ++ ++ if (status & BI) ++ flg = TTY_BREAK; ++ else if (status & PE) ++ flg = TTY_PARITY; ++ else if (status & FE) ++ flg = TTY_FRAME; ++ else ++ flg = TTY_NORMAL; ++ ++ if (uart_handle_sysrq_char(&uart->port, ch)) ++ goto ignore_char; ++ ++ uart_insert_char(&uart->port, status, OE, ch, flg); ++ ++ ignore_char: ++ tty_flip_buffer_push(tty); ++} ++ ++static void ubi32_mailbox_tx_chars(struct ubi32_mailbox_port *uart) ++{ ++ struct circ_buf *xmit = &uart->port.info->xmit; ++ ++ if (uart->port.x_char) { ++ UART_PUT_CHAR(uart, uart->port.x_char); ++ uart->port.icount.tx++; ++ uart->port.x_char = 0; ++ } ++ /* ++ * Check the modem control lines before ++ * transmitting anything. ++ */ ++ ubi32_mailbox_mctrl_check(uart); ++ ++ if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { ++ ubi32_mailbox_stop_tx(&uart->port); ++ return; ++ } ++ ++ while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) { ++ UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ uart->port.icount.tx++; ++ SSYNC(); ++ } ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(&uart->port); ++ ++ if (uart_circ_empty(xmit)) ++ ubi32_mailbox_stop_tx(&uart->port); ++} ++ ++static irqreturn_t ubi32_mailbox_isr(int irq, void *dev_id) ++{ ++ struct ubi32_mailbox_port *uart = dev_id; ++ ++ spin_lock(&uart->port.lock); ++ ++ //XXX?while (UART_GET_LSR(uart) & DR) ++ ++ /* ++ * RX process ++ */ ++ while (ubi32_mailbox_get_char_avail()) { ++ ubi32_mailbox_rx_chars(uart); ++ } ++ ++#if 0 ++ /* ++ * TX process ++ */ ++ if (this_uart.tx_in == this_uart.tx_out) { ++ UBICOM32_IO_PORT(SERIAL_UBICOM_PORT)->int_mask &= ~IO_PORTX_INT_SERDES_TXBE; ++ } else if (UBICOM32_IO_PORT(SERIAL_UBICOM_PORT)->int_status & IO_PORTX_INT_SERDES_TXBE) { ++ uart_ubicom32_send(this_uart.tx_buf[this_uart.tx_out & (SERIAL_UBICOM_BUF_SIZE - 1)]); ++ this_uart.tx_out++; ++ UBICOM32_IO_PORT(SERIAL_UBICOM_PORT)->int_mask |= IO_PORTX_INT_SERDES_TXBE; ++ } ++#endif ++ ++ spin_unlock(&uart->port.lock); ++ ++ return IRQ_HANDLED; ++} ++#if 0 ++static irqreturn_t ubi32_mailbox_tx_int(int irq, void *dev_id) ++{ ++ struct ubi32_mailbox_port *uart = dev_id; ++ ++ spin_lock(&uart->port.lock); ++ if (UART_GET_LSR(uart) & THRE) ++ ubi32_mailbox_tx_chars(uart); ++ spin_unlock(&uart->port.lock); ++ ++ return IRQ_HANDLED; ++} ++#endif ++ ++/* ++ * Return TIOCSER_TEMT when transmitter is not busy. ++ */ ++static unsigned int ubi32_mailbox_tx_empty(struct uart_port *port) ++{ ++// struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ unsigned short lsr; ++ ++ lsr = UART_GET_LSR(uart); ++ if (lsr & TEMT) ++ return TIOCSER_TEMT; ++ else ++ return 0; ++} ++ ++static unsigned int ubi32_mailbox_get_mctrl(struct uart_port *port) ++{ ++ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; ++} ++ ++static void ubi32_mailbox_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++} ++ ++/* ++ * Handle any change of modem status signal since we were last called. ++ */ ++static void ubi32_mailbox_mctrl_check(struct ubi32_mailbox_port *uart) ++{ ++} ++ ++/* ++ * Interrupts are always disabled. ++ */ ++static void ubi32_mailbox_break_ctl(struct uart_port *port, int break_state) ++{ ++// struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ u16 lcr = UART_GET_LCR(uart); ++ if (break_state) ++ lcr |= SB; ++ else ++ lcr &= ~SB; ++ UART_PUT_LCR(uart, lcr); ++ SSYNC(); ++} ++ ++static int ubi32_mailbox_startup(struct uart_port *port) ++{ ++ struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ ++ if (request_irq(uart->port.irq, ubi32_mailbox_isr, IRQF_DISABLED, ++ "UBI32_MAILBOX", uart)) { ++ printk(KERN_NOTICE "Unable to attach Ubicom32 SERDES interrupt\n"); ++ return -EBUSY; ++ } ++ ++ UART_SET_IER(uart, ERBFI); ++ return 0; ++} ++ ++static void ubi32_mailbox_shutdown(struct uart_port *port) ++{ ++ struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ ++ free_irq(uart->port.irq, uart); ++} ++ ++static void ++ubi32_mailbox_set_termios(struct uart_port *port, struct ktermios *termios, ++ struct ktermios *old) ++{ ++ struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ unsigned long flags; ++ unsigned int baud, quot; ++ unsigned short val, ier, lsr, lcr = 0; ++ ++ switch (termios->c_cflag & CSIZE) { ++ case CS8: ++ lcr = WLS(8); ++ break; ++ case CS7: ++ lcr = WLS(7); ++ break; ++ case CS6: ++ lcr = WLS(6); ++ break; ++ case CS5: ++ lcr = WLS(5); ++ break; ++ default: ++ printk(KERN_ERR "%s: word lengh not supported\n", ++ __FUNCTION__); ++ } ++ ++ if (termios->c_cflag & CSTOPB) ++ lcr |= STB; ++ if (termios->c_cflag & PARENB) ++ lcr |= PEN; ++ if (!(termios->c_cflag & PARODD)) ++ lcr |= EPS; ++ if (termios->c_cflag & CMSPAR) ++ lcr |= STP; ++ ++ port->read_status_mask = OE; ++ if (termios->c_iflag & INPCK) ++ port->read_status_mask |= (FE | PE); ++ if (termios->c_iflag & (BRKINT | PARMRK)) ++ port->read_status_mask |= BI; ++ ++ /* ++ * Characters to ignore ++ */ ++ port->ignore_status_mask = 0; ++ if (termios->c_iflag & IGNPAR) ++ port->ignore_status_mask |= FE | PE; ++ if (termios->c_iflag & IGNBRK) { ++ port->ignore_status_mask |= BI; ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns too (for real raw support). ++ */ ++ if (termios->c_iflag & IGNPAR) ++ port->ignore_status_mask |= OE; ++ } ++ ++ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); ++ quot = uart_get_divisor(port, baud); ++ spin_lock_irqsave(&uart->port.lock, flags); ++ ++ do { ++ lsr = UART_GET_LSR(uart); ++ } while (!(lsr & TEMT)); ++ ++ /* Disable UART */ ++ ier = UART_GET_IER(uart); ++ UART_CLEAR_IER(uart, 0xF); ++ ++ UART_PUT_DLL(uart, quot & 0xFF); ++ SSYNC(); ++ UART_PUT_DLH(uart, (quot >> 8) & 0xFF); ++ SSYNC(); ++ ++ UART_PUT_LCR(uart, lcr); ++ ++ /* Enable UART */ ++ UART_SET_IER(uart, ier); ++ ++ val = UART_GET_GCTL(uart); ++ val |= UCEN; ++ UART_PUT_GCTL(uart, val); ++ ++ spin_unlock_irqrestore(&uart->port.lock, flags); ++} ++ ++static const char *ubi32_mailbox_type(struct uart_port *port) ++{ ++ struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ ++ return uart->port.type == PORT_UBI32_MAILBOX ? "UBI32_MAILBOX" : NULL; ++} ++ ++/* ++ * Release the memory region(s) being used by 'port'. ++ */ ++static void ubi32_mailbox_release_port(struct uart_port *port) ++{ ++} ++ ++/* ++ * Request the memory region(s) being used by 'port'. ++ */ ++static int ubi32_mailbox_request_port(struct uart_port *port) ++{ ++ return 0; ++} ++ ++/* ++ * Configure/autoconfigure the port. ++ */ ++static void ubi32_mailbox_config_port(struct uart_port *port, int flags) ++{ ++ struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ ++ if (flags & UART_CONFIG_TYPE && ubi32_mailbox_request_port(&uart->port) == 0) ++ uart->port.type = PORT_UBI32_MAILBOX; ++} ++ ++/* ++ * Verify the new serial_struct (for TIOCSSERIAL). ++ * The only change we allow are to the flags and type, and ++ * even then only between PORT_UBI32_MAILBOX and PORT_UNKNOWN ++ */ ++static int ++ubi32_mailbox_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ return 0; ++} ++ ++static struct uart_ops ubi32_mailbox_pops = { ++ .tx_empty = ubi32_mailbox_tx_empty, ++ .set_mctrl = ubi32_mailbox_set_mctrl, ++ .get_mctrl = ubi32_mailbox_get_mctrl, ++ .stop_tx = ubi32_mailbox_stop_tx, ++ .start_tx = ubi32_mailbox_start_tx, ++ .stop_rx = ubi32_mailbox_stop_rx, ++ .enable_ms = ubi32_mailbox_enable_ms, ++ .break_ctl = ubi32_mailbox_break_ctl, ++ .startup = ubi32_mailbox_startup, ++ .shutdown = ubi32_mailbox_shutdown, ++ .set_termios = ubi32_mailbox_set_termios, ++ .type = ubi32_mailbox_type, ++ .release_port = ubi32_mailbox_release_port, ++ .request_port = ubi32_mailbox_request_port, ++ .config_port = ubi32_mailbox_config_port, ++ .verify_port = ubi32_mailbox_verify_port, ++}; ++ ++static void __init ubi32_mailbox_init_ports(void) ++{ ++ static int first = 1; ++ int i; ++ ++ if (!first) ++ return; ++ first = 0; ++ ++ for (i = 0; i < NR_PORTS; i++) { ++ ubi32_mailbox_ports[i].port.uartclk = get_sclk(); ++ ubi32_mailbox_ports[i].port.ops = &ubi32_mailbox_pops; ++ ubi32_mailbox_ports[i].port.line = i; ++ ubi32_mailbox_ports[i].port.iotype = UPIO_MEM; ++ ubi32_mailbox_ports[i].port.membase = ++ (void __iomem *)ubi32_mailbox_resource[i].uart_base_addr; ++ ubi32_mailbox_ports[i].port.mapbase = ++ ubi32_mailbox_resource[i].uart_base_addr; ++ ubi32_mailbox_ports[i].port.irq = ++ ubi32_mailbox_resource[i].uart_irq; ++ ubi32_mailbox_ports[i].port.flags = UPF_BOOT_AUTOCONF; ++ spin_lock_init(&ubi32_mailbox_ports[i].port.lock); ++ ++ ubi32_mailbox_hw_init(&ubi32_mailbox_ports[i]); ++ } ++ ++} ++ ++#ifdef CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE ++/* ++ * If the port was already initialised (eg, by a boot loader), ++ * try to determine the current setup. ++ */ ++static void __init ++ubi32_mailbox_console_get_options(struct ubi32_mailbox_port *uart, int *baud, ++ int *parity, int *bits) ++{ ++ unsigned short status; ++ ++ status = UART_GET_IER(uart) & (ERBFI | ETBEI); ++ if (status == (ERBFI | ETBEI)) { ++ /* ok, the port was enabled */ ++ unsigned short lcr; ++ unsigned short dlh, dll; ++ ++ lcr = UART_GET_LCR(uart); ++ ++ *parity = 'n'; ++ if (lcr & PEN) { ++ if (lcr & EPS) ++ *parity = 'e'; ++ else ++ *parity = 'o'; ++ } ++ switch (lcr & 0x03) { ++ case 0: *bits = 5; break; ++ case 1: *bits = 6; break; ++ case 2: *bits = 7; break; ++ case 3: *bits = 8; break; ++ } ++ ++ dll = UART_GET_DLL(uart); ++ dlh = UART_GET_DLH(uart); ++ ++ *baud = get_sclk() / (16*(dll | dlh << 8)); ++ } ++ pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__, *baud, *parity, *bits); ++} ++#endif ++ ++#if defined(CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE) || defined(CONFIG_EARLY_PRINTK) ++static struct uart_driver ubi32_mailbox_reg; ++ ++static int __init ++ubi32_mailbox_console_setup(struct console *co, char *options) ++{ ++ struct ubi32_mailbox_port *uart; ++# ifdef CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE ++ int baud = SERIAL_UBICOM_BAUDRATE; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++# endif ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ if (co->index == -1 || co->index >= NR_PORTS) ++ co->index = 0; ++ uart = &ubi32_mailbox_ports[co->index]; ++ ++# ifdef CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ else ++ ubi32_mailbox_console_get_options(uart, &baud, &parity, &bits); ++ ++ //JB return uart_set_options(&uart->port, co, baud, parity, bits, flow); ++ return 0; ++# else ++ return 0; ++# endif ++} ++#endif /* defined (CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE) || ++ defined (CONFIG_EARLY_PRINTK) */ ++ ++#ifdef CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE ++static void ubi32_mailbox_console_putchar(struct uart_port *port, int ch) ++{ ++// struct ubi32_mailbox_port *uart = (struct ubi32_mailbox_port *)port; ++ while (!(UART_GET_LSR(uart) & THRE)) ++ barrier(); ++ UART_PUT_CHAR(uart, ch); ++ SSYNC(); ++} ++ ++/* ++ * Interrupts are disabled on entering ++ */ ++static void ++ubi32_mailbox_console_write(struct console *co, const char *s, unsigned int count) ++{ ++ struct ubi32_mailbox_port *uart = &ubi32_mailbox_ports[co->index]; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&uart->port.lock, flags); ++ uart_console_write(&uart->port, s, count, ubi32_mailbox_console_putchar); ++ spin_unlock_irqrestore(&uart->port.lock, flags); ++ ++} ++ ++static struct console ubi32_mailbox_console = { ++ .name = UBI32_MAILBOX_NAME, ++ .write = ubi32_mailbox_console_write, ++ .device = uart_console_device, ++ .setup = ubi32_mailbox_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++ .data = &ubi32_mailbox_reg, ++}; ++ ++static int __init ubi32_mailbox_console_init(void) ++{ ++ ubi32_mailbox_init_ports(); ++ register_console(&ubi32_mailbox_console); ++ return 0; ++} ++console_initcall(ubi32_mailbox_console_init); ++ ++#define UBI32_MAILBOX_CONSOLE &ubi32_mailbox_console ++#else ++#define UBI32_MAILBOX_CONSOLE NULL ++#endif /* CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE */ ++ ++ ++#ifdef CONFIG_EARLY_PRINTK ++static __init void ubi32_mailbox_early_putc(struct uart_port *port, int ch) ++{ ++ UART_PUT_CHAR(uart, ch); ++} ++ ++static __init void ubi32_mailbox_early_write(struct console *con, const char *s, ++ unsigned int n) ++{ ++ struct ubi32_mailbox_port *uart = &ubi32_mailbox_ports[con->index]; ++ unsigned int i; ++ ++ for (i = 0; i < n; i++, s++) { ++ if (*s == '\n') ++ ubi32_mailbox_early_putc(&uart->port, '\r'); ++ ubi32_mailbox_early_putc(&uart->port, *s); ++ } ++} ++ ++static struct __init console ubi32_mailbox_early_console = { ++ .name = "early_UM", ++ .write = ubi32_mailbox_early_write, ++ .device = uart_console_device, ++ .flags = CON_PRINTBUFFER, ++ .setup = ubi32_mailbox_console_setup, ++ .index = -1, ++ .data = &ubi32_mailbox_reg, ++}; ++ ++/* ++ * XXX Unused in our driver. Need to find out what the termios initialization is good/needed for. ++ */ ++struct console __init *ubi32_mailbox_early_init(unsigned int port, ++ unsigned int cflag) ++{ ++ struct ubi32_mailbox_port *uart; ++ struct ktermios t; ++ ++ if (port == -1 || port >= NR_PORTS) ++ port = 0; ++ ubi32_mailbox_init_ports(); ++ ubi32_mailbox_early_console.index = port; ++ uart = &ubi32_mailbox_ports[port]; ++ t.c_cflag = cflag; ++ t.c_iflag = 0; ++ t.c_oflag = 0; ++ t.c_lflag = ICANON; ++ t.c_line = port; ++ ubi32_mailbox_set_termios(&uart->port, &t, &t); ++ return &ubi32_mailbox_early_console; ++} ++ ++#endif /* CONFIG_SERIAL_UBI32_MAILBOX_CONSOLE */ ++ ++static struct uart_driver ubi32_mailbox_reg = { ++ .owner = THIS_MODULE, ++ .driver_name = "ubi32_mailbox", ++ .dev_name = UBI32_MAILBOX_NAME, ++ .major = UBI32_MAILBOX_MAJOR, ++ .minor = UBI32_MAILBOX_MINOR, ++ .nr = NR_PORTS, ++ .cons = UBI32_MAILBOX_CONSOLE, ++}; ++ ++static int ubi32_mailbox_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct ubi32_mailbox_port *uart = platform_get_drvdata(dev); ++ ++ if (uart) ++ uart_suspend_port(&ubi32_mailbox_reg, &uart->port); ++ ++ return 0; ++} ++ ++static int ubi32_mailbox_resume(struct platform_device *dev) ++{ ++ struct ubi32_mailbox_port *uart = platform_get_drvdata(dev); ++ ++ if (uart) ++ uart_resume_port(&ubi32_mailbox_reg, &uart->port); ++ ++ return 0; ++} ++ ++static int ubi32_mailbox_probe(struct platform_device *dev) ++{ ++ struct resource *res = dev->resource; ++ int i; ++ ++ for (i = 0; i < dev->num_resources; i++, res++) ++ if (res->flags & IORESOURCE_MEM) ++ break; ++ ++ if (i < dev->num_resources) { ++ for (i = 0; i < NR_PORTS; i++, res++) { ++ if (ubi32_mailbox_ports[i].port.mapbase != res->start) ++ continue; ++ ubi32_mailbox_ports[i].port.dev = &dev->dev; ++ uart_add_one_port(&ubi32_mailbox_reg, &ubi32_mailbox_ports[i].port); ++ platform_set_drvdata(dev, &ubi32_mailbox_ports[i]); ++ } ++ } ++ ++ return 0; ++} ++ ++static int ubi32_mailbox_remove(struct platform_device *pdev) ++{ ++ struct ubi32_mailbox_port *uart = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ if (uart) ++ uart_remove_one_port(&ubi32_mailbox_reg, &uart->port); ++ ++ return 0; ++} ++ ++static struct platform_driver ubi32_mailbox_driver = { ++ .probe = ubi32_mailbox_probe, ++ .remove = ubi32_mailbox_remove, ++ .suspend = ubi32_mailbox_suspend, ++ .resume = ubi32_mailbox_resume, ++ .driver = { ++ .name = "ubi32-mbox", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init ubi32_mailbox_init(void) ++{ ++ int ret; ++ ++ pr_info("Serial: Ubicom32 mailbox serial driver.\n"); ++ ++ mailbox_console_flg = TRUE; ++ num_timeouts = 0; ++ ubi32_mailbox_init_ports(); ++ ++ ret = uart_register_driver(&ubi32_mailbox_reg); ++ if (ret == 0) { ++ ret = platform_driver_register(&ubi32_mailbox_driver); ++ if (ret) { ++ pr_debug("uart register failed\n"); ++ uart_unregister_driver(&ubi32_mailbox_reg); ++ } ++ } ++ ++ /* ++ * XXX HACK: currently probe does not get called, but the port needs to be added to work. ++ */ ++ uart_add_one_port(&ubi32_mailbox_reg, &ubi32_mailbox_ports[0].port); ++ return ret; ++} ++ ++static void __exit ubi32_mailbox_exit(void) ++{ ++ platform_driver_unregister(&ubi32_mailbox_driver); ++ uart_unregister_driver(&ubi32_mailbox_reg); ++} ++ ++module_init(ubi32_mailbox_init); ++module_exit(ubi32_mailbox_exit); ++ ++MODULE_ALIAS_CHARDEV_MAJOR(UBI32_MAILBOX_MAJOR); ++MODULE_ALIAS("platform:ubi32_mailbox"); +--- /dev/null ++++ b/drivers/serial/ubi32_serdes.c +@@ -0,0 +1,817 @@ ++/* ++ * drivers/serial/ubi32_serdes.c ++ * Ubicom32 On-Chip Serial Driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/ioport.h> ++#include <linux/init.h> ++#include <linux/console.h> ++#include <linux/sysrq.h> ++#include <linux/platform_device.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++#include <linux/serial_core.h> ++ ++#include <asm/ip5000.h> ++#include <asm/ubicom32suart.h> ++ ++ ++#define SERIAL_UBICOM_PIN_RXD (1 << 0) ++#define SERIAL_UBICOM_PIN_TXD (1 << 6) ++#define SERIAL_UBICOM_CTL0 0x8b300000 ++#define SERIAL_UBICOM_CTL1 0x00000009 ++ ++#define SERIAL_UBICOM_DATA_BIT 8 /* Fixed parameter - do not change */ ++#define SERIAL_UBICOM_PAR_BIT 0 /* Fixed parameter - do not change */ ++#define SERIAL_UBICOM_STOP_BIT 1 /* Fixed parameter - do not change */ ++ ++/* UART name and device definitions */ ++#define UBI32_SERDES_NAME "ttyUS" // XXX ++#define UBI32_SERDES_MAJOR 206 // XXX ++#define UBI32_SERDES_MINOR 64 // XXX ++ ++#define PORT_UBI32_SERDES 1234 ++#define NR_PORTS 1 ++ ++struct uart_port ubi32_serdes_ports[NR_PORTS]; ++ ++struct ubi32_serdes_resource { ++ void *uart_base_addr; ++ int uart_irq; ++ int uart_clock; ++} ubi32_serdes_resource[NR_PORTS] = { ++ /* ++ * Get params from kernel command line (required for early printk) ++ * or from platform resources. ++ */ ++ {0, 0, 0} ++}; ++ ++/* ++ * Can get overridden by 'serdes=' kernel command line. ++ */ ++static int ubi32_serdes_default_baud_rate = 115200; ++ ++ ++#define IO_PORT(port) ((struct ubicom32_io_port *)port->membase) ++#define IO_PORT_INT_STATUS(port) (IO_PORT(port)->int_status) ++#define IO_PORT_INT_MASK(port) (IO_PORT(port)->int_mask) ++#define IO_PORT_INT_CLR(port) (IO_PORT(port)->int_clr) ++ ++ ++/* ++ * ubi32_serdes_get_char() ++ */ ++static u8_t ubi32_serdes_get_char(struct ubicom32_io_port *io_port) ++{ ++ /* ++ * Read from hardware (forced 32-bit atomic read). ++ */ ++ u32_t data = 0; ++ ++ if ( io_port ) { ++ io_port->int_clr = IO_PORTX_INT_SERDES_RXBF; ++ asm volatile ( ++ "move.4 %0, %1 \n\t" ++ : "=r" (data) ++ : "m" (*(u32_t *)&(io_port->rx_fifo)) ++ ); ++ } ++ ++ return (u8_t)(data & 0x000000ff); ++} ++ ++/* ++ * ubi32_serdes_put_char() ++ */ ++static void ubi32_serdes_put_char(struct ubicom32_io_port *io_port, u8_t c) ++{ ++ u32_t data = 0x0000fe00 | (c << 1); ++ ++ if ( io_port ) { ++ /* ++ * Fixed data format: ++ * [LSB]1 start bit - 8 data bits - no parity - 1 stop bit[MSB] ++ */ ++ io_port->int_clr = IO_PORTX_INT_SERDES_TXBE; ++ io_port->ctl2 = data; ++ io_port->int_set = IO_PORTX_INT_SERDES_TXBUF_VALID; ++ } ++} ++ ++static void ubi32_serdes_hw_init(struct uart_port *port, int baud) ++{ ++ struct ubicom32_io_port *io_port = IO_PORT(port); ++ ++ if ( io_port ) { ++ /* ++ * Put port functions 1-4 into reset state. ++ * Function 0 (GPIO) does not need or have a reset bit. ++ * ++ * Select SERDES function for restart below. ++ */ ++ io_port->function = ++ IO_FUNC_FUNCTION_RESET(1) | IO_FUNC_FUNCTION_RESET(2) | ++ IO_FUNC_FUNCTION_RESET(3) | IO_FUNC_FUNCTION_RESET(4) | ++ IO_PORTX_FUNC_SERDES; ++ ++ /* ++ * Configure SERDES baudrate ++ */ ++ if ( baud == 0 ) { ++ baud = ubi32_serdes_default_baud_rate; ++ } ++ ++ io_port->ctl0 = ++ SERIAL_UBICOM_CTL0 | ++ ((port->uartclk / (16 * baud)) - 1); ++ ++ io_port->ctl1 = ++ SERIAL_UBICOM_CTL1; ++ ++ /* ++ * don't interrupt until startup and start_tx ++ */ ++ io_port->int_mask = 0; ++ ++ /* ++ * Set TXD pin output, RXD input and prevent GPIO ++ * override on the TXD & RXD pins ++ */ ++ io_port->gpio_ctl &= ~SERIAL_UBICOM_PIN_RXD; ++ io_port->gpio_ctl |= SERIAL_UBICOM_PIN_TXD; ++ io_port->gpio_mask &= ~(SERIAL_UBICOM_PIN_RXD | SERIAL_UBICOM_PIN_TXD); ++ ++ /* ++ * Restart (un-reset) the port's SERDES function. ++ */ ++ io_port->function &= ~(IO_FUNC_FUNCTION_RESET(IO_PORTX_FUNC_SERDES)); ++ } ++} ++ ++#define ULITE_STATUS_RXVALID IO_PORTX_INT_SERDES_RXBF ++#define ULITE_STATUS_OVERRUN 0 ++#define ULITE_STATUS_FRAME 0 ++#define ULITE_STATUS_PARITY 0 ++#define ULITE_STATUS_TXEMPTY IO_PORTX_INT_SERDES_TXBE ++#define ULITE_STATUS_TXFULL 0 ++ ++static int ubi32_serdes_receive(struct uart_port *port, int stat) ++{ ++ struct tty_struct *tty = port->info->port.tty; ++ unsigned char ch = 0; ++ char flag = TTY_NORMAL; ++ ++ if ((stat & (ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN ++ | ULITE_STATUS_FRAME)) == 0) ++ return 0; ++ ++ /* stats */ ++ if (stat & ULITE_STATUS_RXVALID) { ++ port->icount.rx++; ++ ch = ubi32_serdes_get_char((struct ubicom32_io_port *)port->membase); ++ ++ if (stat & ULITE_STATUS_PARITY) ++ port->icount.parity++; ++ } ++ ++ if (stat & ULITE_STATUS_OVERRUN) ++ port->icount.overrun++; ++ ++ if (stat & ULITE_STATUS_FRAME) ++ port->icount.frame++; ++ ++ ++ /* drop byte with parity error if IGNPAR specificed */ ++ if (stat & port->ignore_status_mask & ULITE_STATUS_PARITY) ++ stat &= ~ULITE_STATUS_RXVALID; ++ ++ stat &= port->read_status_mask; ++ ++ if (stat & ULITE_STATUS_PARITY) ++ flag = TTY_PARITY; ++ ++ stat &= ~port->ignore_status_mask; ++ ++ if (stat & ULITE_STATUS_RXVALID) ++ tty_insert_flip_char(tty, ch, flag); ++ ++ if (stat & ULITE_STATUS_FRAME) ++ tty_insert_flip_char(tty, 0, TTY_FRAME); ++ ++ if (stat & ULITE_STATUS_OVERRUN) ++ tty_insert_flip_char(tty, 0, TTY_OVERRUN); ++ ++ return 1; ++} ++ ++/* ++ * interrupts are disabled on entry ++ */ ++static void ubi32_serdes_stop_tx(struct uart_port *port) ++{ ++ IO_PORT_INT_MASK(port) = IO_PORT_INT_MASK(port) & ~IO_PORTX_INT_SERDES_TXBE; ++} ++ ++static int ubi32_serdes_transmit(struct uart_port *port, int stat) ++{ ++ struct circ_buf *xmit = &port->info->xmit; ++ ++ if (!(stat & IO_PORTX_INT_SERDES_TXBE)) ++ return 0; ++ ++ if (port->x_char) { ++ ubi32_serdes_put_char(IO_PORT(port), port->x_char); ++ port->x_char = 0; ++ port->icount.tx++; ++ return 1; ++ } ++ ++ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { ++ ubi32_serdes_stop_tx(port); ++ return 0; ++ } ++ ++ ubi32_serdes_put_char(IO_PORT(port), xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); ++ port->icount.tx++; ++ ++ /* wake up */ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ if (uart_circ_empty(xmit)) ++ ubi32_serdes_stop_tx(port); ++ ++ return 1; ++} ++ ++/* ++ * port is locked and interrupts are disabled ++ */ ++static void ubi32_serdes_start_tx(struct uart_port *port) ++{ ++ IO_PORT_INT_MASK(port) = IO_PORT_INT_MASK(port) | IO_PORTX_INT_SERDES_TXBE; ++ ubi32_serdes_transmit(port, IO_PORT_INT_STATUS(port)); ++} ++ ++/* ++ * Interrupts are enabled ++ */ ++static void ubi32_serdes_stop_rx(struct uart_port *port) ++{ ++ /* don't forward any more data (like !CREAD) */ ++ port->ignore_status_mask = IO_PORTX_INT_SERDES_RXBF; ++} ++ ++/* ++ * Set the modem control timer to fire immediately. ++ */ ++static void ubi32_serdes_enable_ms(struct uart_port *port) ++{ ++ /* N/A */ ++} ++ ++static irqreturn_t ubi32_serdes_isr(int irq, void *dev_id) ++{ ++ struct uart_port *port = dev_id; ++ int busy; ++ ++ spin_lock(&port->lock); ++ ++ do { ++ int stat = IO_PORT_INT_STATUS(port); ++ busy = ubi32_serdes_receive(port, stat); ++ busy |= ubi32_serdes_transmit(port, stat); ++ } while (busy); ++ ++ tty_flip_buffer_push(port->info->port.tty); ++ ++ spin_unlock(&port->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Return TIOCSER_TEMT when transmitter is not busy. ++ */ ++static unsigned int ubi32_serdes_tx_empty(struct uart_port *port) ++{ ++ unsigned long flags; ++ unsigned int ret; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ret = IO_PORT_INT_STATUS(port); ++ spin_unlock_irqrestore(&port->lock, flags); ++ ++ return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; ++} ++ ++static unsigned int ubi32_serdes_get_mctrl(struct uart_port *port) ++{ ++ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; ++} ++ ++static void ubi32_serdes_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ /* N/A */ ++} ++ ++/* ++ * Interrupts are always disabled. ++ */ ++static void ubi32_serdes_break_ctl(struct uart_port *port, int break_state) ++{ ++ /* N/A */ ++} ++ ++static int ubi32_serdes_startup(struct uart_port *port) ++{ ++ if (request_irq(port->irq, ubi32_serdes_isr, IRQF_DISABLED, ++ "UBI32_SERDES", port)) { ++ printk(KERN_NOTICE "Unable to attach port interrupt\n"); ++ return -EBUSY; ++ } ++ ++ IO_PORT_INT_CLR(port) = IO_PORTX_INT_SERDES_RXBF; ++ IO_PORT_INT_MASK(port) = IO_PORTX_INT_SERDES_RXBF; ++ return 0; ++} ++ ++static void ubi32_serdes_shutdown(struct uart_port *port) ++{ ++ struct ubi32_serdes_port *uart = (struct ubi32_serdes_port *)port; ++ ++ IO_PORT_INT_MASK(port) = 0; ++ free_irq(port->irq, uart); ++} ++ ++static void ++ubi32_serdes_set_termios(struct uart_port *port, struct ktermios *termios, ++ struct ktermios *old) ++{ ++ unsigned long flags; ++ unsigned int baud; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++ port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN ++ | ULITE_STATUS_TXFULL; ++ ++ if (termios->c_iflag & INPCK) ++ port->read_status_mask |= ++ ULITE_STATUS_PARITY | ULITE_STATUS_FRAME; ++ ++ port->ignore_status_mask = 0; ++ if (termios->c_iflag & IGNPAR) ++ port->ignore_status_mask |= ULITE_STATUS_PARITY ++ | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN; ++ ++ /* ignore all characters if CREAD is not set */ ++ if ((termios->c_cflag & CREAD) == 0) ++ port->ignore_status_mask |= ++ ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY ++ | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN; ++ ++ /* update timeout */ ++ baud = uart_get_baud_rate(port, termios, old, 0, 460800); ++ uart_update_timeout(port, termios->c_cflag, baud); ++ ++ IO_PORT(port)->ctl0 = SERIAL_UBICOM_CTL0 | ++ ((port->uartclk / (16 * baud)) - 1); ++ ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static const char *ubi32_serdes_type(struct uart_port *port) ++{ ++ return port->type == PORT_UBI32_SERDES ? "UBI32_SERDES" : NULL; ++} ++ ++/* ++ * Release the memory region(s) being used by 'port'. ++ */ ++static void ubi32_serdes_release_port(struct uart_port *port) ++{ ++} ++ ++/* ++ * Request the memory region(s) being used by 'port'. ++ */ ++static int ubi32_serdes_request_port(struct uart_port *port) ++{ ++ return 0; ++} ++ ++/* ++ * Configure/autoconfigure the port. ++ */ ++static void ubi32_serdes_config_port(struct uart_port *port, int flags) ++{ ++ if (flags & UART_CONFIG_TYPE && ++ ubi32_serdes_request_port(port) == 0) ++ port->type = PORT_UBI32_SERDES; ++} ++ ++/* ++ * Verify the new serial_struct (for TIOCSSERIAL). ++ * The only change we allow are to the flags and type, and ++ * even then only between PORT_UBI32_SERDES and PORT_UNKNOWN ++ */ ++static int ++ubi32_serdes_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ return 0; ++} ++ ++static struct uart_ops ubi32_serdes_pops = { ++ .tx_empty = ubi32_serdes_tx_empty, ++ .set_mctrl = ubi32_serdes_set_mctrl, ++ .get_mctrl = ubi32_serdes_get_mctrl, ++ .stop_tx = ubi32_serdes_stop_tx, ++ .start_tx = ubi32_serdes_start_tx, ++ .stop_rx = ubi32_serdes_stop_rx, ++ .enable_ms = ubi32_serdes_enable_ms, ++ .break_ctl = ubi32_serdes_break_ctl, ++ .startup = ubi32_serdes_startup, ++ .shutdown = ubi32_serdes_shutdown, ++ .set_termios = ubi32_serdes_set_termios, ++ .type = ubi32_serdes_type, ++ .release_port = ubi32_serdes_release_port, ++ .request_port = ubi32_serdes_request_port, ++ .config_port = ubi32_serdes_config_port, ++ .verify_port = ubi32_serdes_verify_port, ++}; ++ ++static void __init ubi32_serdes_init_ports(void) ++{ ++ int i; ++ ++ for (i = 0; i < NR_PORTS; i++) { ++ ubi32_serdes_ports[i].uartclk = ubi32_serdes_resource[i].uart_clock; ++ ubi32_serdes_ports[i].ops = &ubi32_serdes_pops; ++ ubi32_serdes_ports[i].line = i; ++ ubi32_serdes_ports[i].iotype = UPIO_MEM; ++ ubi32_serdes_ports[i].membase = ++ (void __iomem *)ubi32_serdes_resource[i].uart_base_addr; ++ ubi32_serdes_ports[i].mapbase = ++ (resource_size_t)ubi32_serdes_resource[i].uart_base_addr; ++ ubi32_serdes_ports[i].irq = ++ ubi32_serdes_resource[i].uart_irq; ++ ubi32_serdes_ports[i].flags = UPF_BOOT_AUTOCONF; ++ ++ ubi32_serdes_hw_init(&ubi32_serdes_ports[i], 0); ++ } ++ ++} ++ ++#ifdef CONFIG_SERIAL_UBI32_SERDES_CONSOLE ++/* ++ * If the port was already initialised (eg, by a boot loader), ++ * try to determine the current setup. ++ */ ++static void __init ++ubi32_serdes_console_get_options(struct uart_port *port, int *baud) ++{ ++ u32 round_to = 1200; ++ u32 real_baud; ++ ++ /* ++ * We might get called before platform init and with no ++ * kernel command line options, so port might be NULL. ++ */ ++ *baud = ubi32_serdes_default_baud_rate;; ++ if ( IO_PORT(port) == 0 ) ++ return; ++ ++ real_baud = port->uartclk ++ / (16 * ((IO_PORT(port)->ctl0 & ~SERIAL_UBICOM_CTL0) + 1)); ++ ++ *baud = ((real_baud + round_to - 1) / round_to) * round_to; ++ ++ pr_debug("%s:baud = %d, real_baud = %d\n", __FUNCTION__, *baud, real_baud); ++} ++#endif ++ ++#if defined(CONFIG_SERIAL_UBI32_SERDES_CONSOLE) || defined(CONFIG_EARLY_PRINTK) ++static struct uart_driver ubi32_serdes_reg; ++ ++static int __init ++ubi32_serdes_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++#ifdef CONFIG_SERIAL_UBI32_SERDES_CONSOLE ++ int baud = ubi32_serdes_default_baud_rate; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++#endif ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ if (co->index == -1 || co->index >= NR_PORTS) ++ co->index = 0; ++ port = &ubi32_serdes_ports[co->index]; ++ ++#ifdef CONFIG_SERIAL_UBI32_SERDES_CONSOLE ++ if (options) { ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ ubi32_serdes_hw_init(port, baud); ++ } ++ else ++ ubi32_serdes_console_get_options(port, &baud); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++#else ++ return 0; ++#endif ++} ++#endif /* defined (CONFIG_SERIAL_UBI32_SERDES_CONSOLE) || ++ defined (CONFIG_EARLY_PRINTK) */ ++ ++#ifdef CONFIG_SERIAL_UBI32_SERDES_CONSOLE ++static void ++ubi32_serdes_console_putchar(struct uart_port *port, int ch) ++{ ++ if ( IO_PORT(port) ) { ++ while (!(IO_PORT_INT_STATUS(port) & IO_PORTX_INT_SERDES_TXBE)) ++ barrier(); ++ ubi32_serdes_put_char(IO_PORT(port), ch); ++ } ++} ++ ++/* ++ * Interrupts are disabled on entering ++ */ ++static void ++ubi32_serdes_console_write(struct console *co, const char *s, unsigned int count) ++{ ++ struct uart_port *port = &ubi32_serdes_ports[co->index]; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ uart_console_write(port, s, count, ubi32_serdes_console_putchar); ++ spin_unlock_irqrestore(&port->lock, flags); ++ ++} ++ ++static struct console ubi32_serdes_console = { ++ .name = UBI32_SERDES_NAME, ++ .write = ubi32_serdes_console_write, ++ .device = uart_console_device, ++ .setup = ubi32_serdes_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++ .data = &ubi32_serdes_reg, ++}; ++ ++static int __init ubi32_serdes_console_init(void) ++{ ++ ubi32_serdes_init_ports(); ++ register_console(&ubi32_serdes_console); ++ return 0; ++} ++console_initcall(ubi32_serdes_console_init); ++ ++#define UBI32_SERDES_CONSOLE &ubi32_serdes_console ++#else ++#define UBI32_SERDES_CONSOLE NULL ++#endif /* CONFIG_SERIAL_UBI32_SERDES_CONSOLE */ ++ ++ ++#ifdef CONFIG_EARLY_PRINTK ++static __init void ubi32_serdes_early_putc(struct uart_port *port, int ch) ++{ ++ unsigned timeout = 0xffff; ++ ++ while ((!(IO_PORT_INT_STATUS(port) & IO_PORTX_INT_SERDES_TXBE)) && --timeout) ++ cpu_relax(); ++ ubi32_serdes_put_char(IO_PORT(port), ch); ++} ++ ++static __init void ubi32_serdes_early_write(struct console *con, const char *s, ++ unsigned int n) ++{ ++ struct uart_port *port = &ubi32_serdes_ports[con->index]; ++ unsigned int i; ++ ++ for (i = 0; i < n; i++, s++) { ++ if (*s == '\n') ++ ubi32_serdes_early_putc(port, '\r'); ++ ubi32_serdes_early_putc(port, *s); ++ } ++} ++ ++static struct __init console ubi32_serdes_early_console = { ++ .name = "early_US", ++ .write = ubi32_serdes_early_write, ++ .device = uart_console_device, ++ .flags = CON_PRINTBUFFER, ++ .setup = ubi32_serdes_console_setup, ++ .index = -1, ++ .data = &ubi32_serdes_reg, ++}; ++ ++/* ++ * XXX Unused in our driver. Need to find out what the termios initialization is good/needed for. ++ */ ++struct console __init *ubi32_serdes_early_init(unsigned int port_index, ++ unsigned int cflag) ++{ ++ struct uart_port *uart; ++ struct ktermios t; ++ ++ if (port_index == -1 || port_index >= NR_PORTS) ++ port_index = 0; ++ ubi32_serdes_init_ports(); ++ ubi32_serdes_early_console.index = port_index; ++ uart = &ubi32_serdes_ports[port_index]; ++ t.c_cflag = cflag; ++ t.c_iflag = 0; ++ t.c_oflag = 0; ++ t.c_lflag = ICANON; ++ t.c_line = port_index; ++ ubi32_serdes_set_termios(uart, &t, &t); ++ return &ubi32_serdes_early_console; ++} ++ ++#endif /* CONFIG_SERIAL_UBI32_SERDES_CONSOLE */ ++ ++static struct uart_driver ubi32_serdes_reg = { ++ .owner = THIS_MODULE, ++ .driver_name = "ubi32_serdes", ++ .dev_name = UBI32_SERDES_NAME, ++ .major = UBI32_SERDES_MAJOR, ++ .minor = UBI32_SERDES_MINOR, ++ .nr = NR_PORTS, ++ .cons = UBI32_SERDES_CONSOLE, ++}; ++ ++static int ubi32_serdes_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct uart_port *port = platform_get_drvdata(dev); ++ ++ if (port) ++ uart_suspend_port(&ubi32_serdes_reg, port); ++ ++ return 0; ++} ++ ++static int ubi32_serdes_resume(struct platform_device *dev) ++{ ++ struct uart_port *port = platform_get_drvdata(dev); ++ ++ if (port) ++ uart_resume_port(&ubi32_serdes_reg, port); ++ ++ return 0; ++} ++ ++static int ubi32_serdes_probe(struct platform_device *dev) ++{ ++ struct resource *res = dev->resource; ++ int i; ++ ++ for (i = 0; i < dev->num_resources; i++, res++) { ++ if (res->flags & IORESOURCE_MEM) { ++ ubi32_serdes_resource[0].uart_base_addr = (void *) res->start; ++ } ++ else if (res->flags & IORESOURCE_IRQ) { ++ ubi32_serdes_resource[0].uart_irq = res->start; ++ } ++ else if (res->flags & UBICOM32_SUART_IORESOURCE_CLOCK) { ++ ubi32_serdes_resource[0].uart_clock = res->start; ++ } ++ } ++ ++ ubi32_serdes_init_ports(); ++ ++ return 0; ++} ++ ++static int ubi32_serdes_remove(struct platform_device *pdev) ++{ ++ struct uart_port *port = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ if (port) ++ uart_remove_one_port(&ubi32_serdes_reg, port); ++ ++ return 0; ++} ++ ++static struct platform_driver ubi32_serdes_driver = { ++ .remove = ubi32_serdes_remove, ++ .suspend = ubi32_serdes_suspend, ++ .resume = ubi32_serdes_resume, ++ .driver = { ++ .name = "ubicom32suart", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++ ++#ifndef MODULE ++/* ++ * Called at boot time. ++ * ++ * You can specify IO base, IRQ, and clock for the serdes serial port ++ * using kernel command line "serdes=0xiobase,irq,clock". Values ++ * specified will be overwritten by platform device data, if present. ++ */ ++static int __init ubi32_serdes_setup(char *str) ++{ ++#define N_PARMS (4+1) ++ int ints[N_PARMS]; ++ int i; ++ ++ str = get_options(str, ARRAY_SIZE(ints), ints); ++ ++ for (i = 0; i < N_PARMS; i++) { ++ if (i < ints[0]) { ++ if (i == 0) { ++ ubi32_serdes_resource[0].uart_base_addr = (void *) ints[i+1]; ++ } ++ else if (i == 1) { ++ ubi32_serdes_resource[0].uart_irq = ints[i+1]; ++ } ++ else if (i == 2) { ++ ubi32_serdes_resource[0].uart_clock = ints[i+1]; ++ } ++ else if (i == 3) { ++ ubi32_serdes_default_baud_rate = ints[i+1]; ++ } ++ } ++ } ++ return 1; ++} ++ ++__setup("serdes=", ubi32_serdes_setup); ++#endif ++ ++static int __init ubi32_serdes_init(void) ++{ ++ int ret; ++ ++ pr_info("Serial: Ubicom32 serdes uart serial driver\n"); ++ ++ ret = platform_driver_probe(&ubi32_serdes_driver, ubi32_serdes_probe); ++ if (ret != 0) { ++ printk(KERN_INFO "serdes platform_driver_probe() failed: %d\n", ret); ++ return ret; ++ } ++ ++ ubi32_serdes_init_ports(); ++ ++ ret = uart_register_driver(&ubi32_serdes_reg); ++ if ( ret == 0 ) { ++ ret = uart_add_one_port(&ubi32_serdes_reg, &ubi32_serdes_ports[0]); ++ if ( ret != 0 ) { ++ uart_unregister_driver(&ubi32_serdes_reg); ++ } ++ } ++ ++ return ret; ++} ++ ++static void __exit ubi32_serdes_exit(void) ++{ ++ platform_driver_unregister(&ubi32_serdes_driver); ++ uart_unregister_driver(&ubi32_serdes_reg); ++} ++ ++module_init(ubi32_serdes_init); ++module_exit(ubi32_serdes_exit); ++ ++MODULE_AUTHOR("Rainer Keller <rkeller@ubicom.com>"); ++MODULE_DESCRIPTION("Ubicom generic serial port driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_CHARDEV_MAJOR(UBI32_SERDES_MAJOR); ++MODULE_ALIAS("platform:ubi32_serdes"); +--- /dev/null ++++ b/drivers/serial/ubi32_uarttio.c +@@ -0,0 +1,1171 @@ ++/* ++ * drivers/serial/ubi32_uarttio.c ++ * Ubicom32 Serial Virtual Peripherial Driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/module.h> ++#include <linux/ioport.h> ++#include <linux/init.h> ++#include <linux/console.h> ++#include <linux/sysrq.h> ++#include <linux/platform_device.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++#include <linux/serial_core.h> ++ ++#include <asm/ip5000.h> ++#include <asm/gpio.h> ++#include <asm/thread.h> ++#include <asm/uart_tio.h> ++ ++#define DRIVER_NAME "ubi32_uarttio" ++ ++/* ++ * For storing the module parameters. ++ */ ++#define UBI32_UARTTIO_MAX_PARAM_LEN 80 ++static char utio_ports_param[UBI32_UARTTIO_MAX_PARAM_LEN]; ++ ++/* ++ * UART name and device definitions ++ */ ++#define UBI32_UARTTIO_NAME "ttyUV" // XXX ++#define UBI32_UARTTIO_MAJOR 206 // XXX ++#define UBI32_UARTTIO_MINOR 64 // XXX ++ ++/* ++ * The following structures are allocated statically because the ++ * memory allocation subsystem is not initialized this early on ++ */ ++ ++/* ++ * Per port structure ++ */ ++struct ubi32_uarttio_port { ++ struct uarttio_uart *uart; ++ unsigned int tx_pin; ++ unsigned int rx_pin; ++ ++ struct uart_port port; ++ ++ u8_t added; ++ ++ /* ++ * If this value is set, the port has had its direction set already ++ */ ++ u8_t port_init; ++}; ++static struct ubi32_uarttio_port uarttio_ports[CONFIG_SERIAL_UBI32_UARTTIO_NR_UARTS]; ++ ++/* ++ * Number of ports currently initialized ++ */ ++static int uarttio_nports; ++ ++/* ++ * Per device structure ++ */ ++struct ubi32_uarttio_instance { ++ struct uarttio_regs *regs; ++ struct ubi32_uarttio_port *ports; ++ ++ u8_t irq_requested; ++ u8_t driver_registered; ++ u8_t irq; ++}; ++static struct ubi32_uarttio_instance uarttio_inst; ++ ++#ifdef CONFIG_SERIAL_UBI32_UARTTIO_CONSOLE ++static struct console ubi32_uarttio_console; ++#define UBI32_UARTTIO_CONSOLE &ubi32_uarttio_console ++#else ++#define UBI32_UARTTIO_CONSOLE NULL ++#endif ++ ++static struct uart_driver ubi32_uarttio_uart_driver = { ++ .owner = THIS_MODULE, ++ .driver_name = DRIVER_NAME, ++ .dev_name = UBI32_UARTTIO_NAME, ++ .major = UBI32_UARTTIO_MAJOR, ++ .minor = UBI32_UARTTIO_MINOR, ++ .cons = UBI32_UARTTIO_CONSOLE, ++}; ++ ++#ifdef UBI32_UARTTIO_UNUSED ++/* ++ * ubi32_uarttio_get_send_space ++ */ ++static int ubi32_uarttio_get_send_space(struct uarttio_uart *uart) ++{ ++ int count = uart->tx_fifo_head - uart->tx_fifo_tail; ++ if (count < 0) { ++ count += uart->tx_fifo_size; ++ } ++ return uart->tx_fifo_size - count; ++} ++#endif ++ ++/* ++ * ubi32_uarttio_get_recv_ready ++ */ ++static int ubi32_uarttio_get_recv_ready(struct uarttio_uart *uart) ++{ ++ int count = uart->rx_fifo_head - uart->rx_fifo_tail; ++ if (count < 0) { ++ count += uart->rx_fifo_size; ++ } ++ return count; ++} ++ ++/* ++ * ubi32_uarttio_get_char() ++ */ ++static u8_t ubi32_uarttio_get_char(struct uarttio_uart *uart) ++{ ++ /* ++ * Retrieve byte ++ */ ++ u32_t tail = uart->rx_fifo_tail; ++ u8_t data = uart->rx_fifo[tail]; ++ ++ if (++tail == uart->rx_fifo_size) { ++ tail = 0; ++ } ++ uart->rx_fifo_tail = tail; ++ ++ return data; ++} ++ ++/* ++ * ubi32_uarttio_put_char() ++ */ ++static int ubi32_uarttio_put_char(struct uarttio_uart *uart, u8_t c) ++{ ++ u32_t head = uart->tx_fifo_head; ++ u32_t prev = head; ++ ++ /* ++ * Wrap ++ */ ++ if (++head == uart->tx_fifo_size) { ++ head = 0; ++ } ++ ++ /* ++ * If there isn't any space, return EBUSY ++ */ ++ if (head == uart->tx_fifo_tail) { ++ return -EBUSY; ++ } ++ ++ /* ++ * Put the character in the queue ++ */ ++ uart->tx_fifo[prev] = c; ++ uart->tx_fifo_head = head; ++ ++ return 0; ++} ++ ++/* ++ * ubi32_uarttio_set_baud ++ */ ++static int ubi32_uarttio_set_baud(struct ubi32_uarttio_port *uup, unsigned int baud) ++{ ++ if (uup->uart->current_baud_rate == baud) { ++ return 0; ++ } ++ ++ uup->uart->baud_rate = baud; ++ uup->uart->flags |= UARTTIO_UART_FLAG_SET_RATE; ++ while (uup->uart->flags & UARTTIO_UART_FLAG_SET_RATE) { ++ cpu_relax(); ++ } ++ ++ if (uup->uart->current_baud_rate != baud) { ++ /* ++ * Failed to set baud rate ++ */ ++ printk(KERN_WARNING "Invalid baud rate %u, running at %u\n", baud, uup->uart->current_baud_rate); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ubi32_uarttio_handle_receive ++ */ ++static void ubi32_uarttio_handle_receive(struct ubi32_uarttio_port *uup, int stat) ++{ ++ struct uarttio_uart *uart = uup->uart; ++ struct uart_port *port = &uup->port; ++ struct tty_struct *tty = port->info->port.tty; ++ unsigned char ch = 0; ++ char flag = TTY_NORMAL; ++ int count; ++ ++ if ((stat & (UARTTIO_UART_INT_RX | UARTTIO_UART_INT_RXFRAME | UARTTIO_UART_INT_RXOVF)) == 0) { ++ return; ++ } ++ ++ if (stat & UARTTIO_UART_INT_RX) { ++ count = ubi32_uarttio_get_recv_ready(uart); ++ port->icount.rx += count; ++ } ++ ++ if (stat & UARTTIO_UART_INT_RXOVF) { ++ port->icount.overrun++; ++ } ++ ++ if (stat & UARTTIO_UART_INT_RXFRAME) { ++ port->icount.frame++; ++ } ++ ++ stat &= ~port->ignore_status_mask; ++ ++ if (stat & UARTTIO_UART_INT_RX) { ++ int i; ++ for (i = 0; i < count; i++) { ++ ch = ubi32_uarttio_get_char(uart); ++ tty_insert_flip_char(tty, ch, flag); ++ } ++ } ++ ++ if (stat & UARTTIO_UART_INT_RXFRAME) { ++ tty_insert_flip_char(tty, 0, TTY_FRAME); ++ } ++ ++ if (stat & UARTTIO_UART_INT_RXOVF) { ++ tty_insert_flip_char(tty, 0, TTY_OVERRUN); ++ } ++} ++ ++/* ++ * ubi32_uarttio_stop_tx ++ * interrupts are disabled on entry ++ */ ++static void ubi32_uarttio_stop_tx(struct uart_port *port) ++{ ++ struct ubi32_uarttio_port *uup = port->private_data; ++ ++ uup->uart->int_mask &= ~UARTTIO_UART_INT_TXBE; ++} ++ ++/* ++ * ubi32_uarttio_handle_transmit ++ */ ++static void ubi32_uarttio_handle_transmit(struct ubi32_uarttio_port *uup, int stat) ++{ ++ struct uarttio_uart *uart = uup->uart; ++ struct uart_port *port = &uup->port; ++ struct circ_buf *xmit = &port->info->xmit; ++ ++ if (!(stat & UARTTIO_UART_INT_TXBE)) { ++ return; ++ } ++ ++ if (port->x_char) { ++ if (ubi32_uarttio_put_char(uart, port->x_char)) { ++ return; ++ } ++ port->x_char = 0; ++ port->icount.tx++; ++ return; ++ } ++ ++ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { ++ ubi32_uarttio_stop_tx(port); ++ return; ++ } ++ ++ /* ++ * Send as many characters as we can ++ */ ++ while (ubi32_uarttio_put_char(uart, xmit->buf[xmit->tail]) == 0) { ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (uart_circ_empty(xmit)) { ++ break; ++ } ++ } ++ ++ /* wake up */ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { ++ uart_write_wakeup(port); ++ } ++ ++ if (uart_circ_empty(xmit)) { ++ ubi32_uarttio_stop_tx(port); ++ } ++} ++ ++/* ++ * ubi32_uarttio_start_tx ++ * port is locked and interrupts are disabled ++ */ ++static void ubi32_uarttio_start_tx(struct uart_port *port) ++{ ++ struct ubi32_uarttio_port *uup = port->private_data; ++ struct uarttio_uart *uart = uup->uart; ++ ++ uart->int_mask |= UARTTIO_UART_INT_TXBE; ++} ++ ++/* ++ * ubi32_uarttio_stop_rx ++ * Interrupts are enabled ++ */ ++static void ubi32_uarttio_stop_rx(struct uart_port *port) ++{ ++ struct ubi32_uarttio_port *uup = port->private_data; ++ struct uarttio_uart *uart = uup->uart; ++ ++ /* ++ * don't forward any more data (like !CREAD) ++ */ ++ uart->int_mask &= ~UARTTIO_UART_INT_RX; ++ port->ignore_status_mask = UARTTIO_UART_INT_RX; ++} ++ ++/* ++ * ubi32_uarttio_enable_ms ++ * Set the modem control timer to fire immediately. ++ */ ++static void ubi32_uarttio_enable_ms(struct uart_port *port) ++{ ++ /* N/A */ ++} ++ ++/* ++ * ubi32_uarttio_isr ++ */ ++static irqreturn_t ubi32_uarttio_isr(int irq, void *appdata) ++{ ++ struct ubi32_uarttio_port *uup = uarttio_ports; ++ int i; ++ ++ /* ++ * Service all of the ports ++ */ ++ for (i = 0; i < uarttio_nports; i++) { ++ unsigned int flags; ++ ++ if (!(uup->uart->flags & UARTTIO_UART_FLAG_ENABLED)) { ++ uup++; ++ continue; ++ } ++ ++ spin_lock(&uup->port.lock); ++ ++ flags = uup->uart->int_flags; ++ ++ uup->uart->int_flags = 0; ++ ++ ubi32_uarttio_handle_receive(uup, flags); ++ ubi32_uarttio_handle_transmit(uup, flags); ++ ++ tty_flip_buffer_push(uup->port.info->port.tty); ++ ++ spin_unlock(&uup->port.lock); ++ ++ uup++; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * ubi32_uarttio_tx_empty ++ * Return TIOCSER_TEMT when transmitter is not busy. ++ */ ++static unsigned int ubi32_uarttio_tx_empty(struct uart_port *port) ++{ ++ struct ubi32_uarttio_port *uup = port->private_data; ++ ++ if (uup->uart->tx_fifo_head == uup->uart->tx_fifo_tail) { ++ return TIOCSER_TEMT; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ubi32_uarttio_get_mctrl ++ */ ++static unsigned int ubi32_uarttio_get_mctrl(struct uart_port *port) ++{ ++ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; ++} ++ ++/* ++ * ubi32_uarttio_set_mctrl ++ */ ++static void ubi32_uarttio_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ /* N/A */ ++} ++ ++/* ++ * ubi32_uarttio_break_ctl ++ */ ++static void ubi32_uarttio_break_ctl(struct uart_port *port, int break_state) ++{ ++ /* N/A */ ++} ++ ++/* ++ * ubi32_uarttio_startup ++ */ ++static int ubi32_uarttio_startup(struct uart_port *port) ++{ ++ struct ubi32_uarttio_port *uup = port->private_data; ++ struct uarttio_uart *uart = uup->uart; ++ ++ uart->flags |= UARTTIO_UART_FLAG_ENABLED; ++ ++ uart->int_mask |= UARTTIO_UART_INT_TXBE | UARTTIO_UART_INT_RX; ++ ++ return 0; ++} ++ ++/* ++ * ubi32_uarttio_shutdown ++ */ ++static void ubi32_uarttio_shutdown(struct uart_port *port) ++{ ++ struct ubi32_uarttio_port *uup = port->private_data; ++ struct uarttio_uart *uart = uup->uart; ++ ++ uart->int_mask = 0; ++ uart->flags &= ~UARTTIO_UART_FLAG_ENABLED; ++} ++ ++/* ++ * ubi32_uarttio_set_termios ++ */ ++static void ubi32_uarttio_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) ++{ ++ struct ubi32_uarttio_port *uup = port->private_data; ++ unsigned long flags; ++ unsigned int baud; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++#if 0 ++ port->read_status_mask = UBI32_UARTTIO_RX | UBI32_UARTTIO_RXOVF | UBI32_UARTTIO_TXOVF; ++ ++ if (termios->c_iflag & INPCK) { ++ port->read_status_mask |= UBI32_UARTTIO_RXFRAME; ++ } ++#endif ++ ++ port->ignore_status_mask = 0; ++ if (termios->c_iflag & IGNPAR) { ++ port->ignore_status_mask |= UARTTIO_UART_INT_RXFRAME | ++ UARTTIO_UART_INT_RXOVF; ++ } ++ ++ /* ++ * ignore all characters if CREAD is not set ++ */ ++ if ((termios->c_cflag & CREAD) == 0) { ++ port->ignore_status_mask |= UARTTIO_UART_INT_RX | ++ UARTTIO_UART_INT_RXFRAME | ++ UARTTIO_UART_INT_RXOVF; ++ } ++ ++ /* update timeout */ ++ baud = uart_get_baud_rate(port, termios, old, 0, 460800); ++ uart_update_timeout(port, termios->c_cflag, baud); ++ ++ ubi32_uarttio_set_baud(uup, baud); ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++/* ++ * ubi32_uarttio_type ++ */ ++static const char *ubi32_uarttio_type(struct uart_port *port) ++{ ++ return (port->type == PORT_UBI32_UARTTIO) ? "UBI32_UARTTIO" : NULL; ++} ++ ++/* ++ * ubi32_uarttio_release_port ++ * Release the memory region(s) being used by 'port'. ++ */ ++static void ubi32_uarttio_release_port(struct uart_port *port) ++{ ++} ++ ++/* ++ * ubi32_uarttio_request_port ++ * Request the memory region(s) being used by 'port'. ++ */ ++static int ubi32_uarttio_request_port(struct uart_port *port) ++{ ++ return 0; ++} ++ ++/* ++ * ubi32_uarttio_config_port ++ * Configure/autoconfigure the port. ++ */ ++static void ubi32_uarttio_config_port(struct uart_port *port, int flags) ++{ ++ if ((flags & UART_CONFIG_TYPE) && (ubi32_uarttio_request_port(port) == 0)) { ++ port->type = PORT_UBI32_UARTTIO; ++ } ++} ++ ++/* ++ * ubi32_uarttio_verify_port ++ * Verify the new serial_struct (for TIOCSSERIAL). ++ * ++ * The only change we allow are to the flags and type, and ++ * even then only between PORT_UBI32_UARTTIO and PORT_UNKNOWN ++ */ ++static int ubi32_uarttio_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ return 0; ++} ++ ++static struct uart_ops ubi32_uarttio_pops = { ++ .tx_empty = ubi32_uarttio_tx_empty, ++ .set_mctrl = ubi32_uarttio_set_mctrl, ++ .get_mctrl = ubi32_uarttio_get_mctrl, ++ .stop_tx = ubi32_uarttio_stop_tx, ++ .start_tx = ubi32_uarttio_start_tx, ++ .stop_rx = ubi32_uarttio_stop_rx, ++ .enable_ms = ubi32_uarttio_enable_ms, ++ .break_ctl = ubi32_uarttio_break_ctl, ++ .startup = ubi32_uarttio_startup, ++ .shutdown = ubi32_uarttio_shutdown, ++ .set_termios = ubi32_uarttio_set_termios, ++ .type = ubi32_uarttio_type, ++ .release_port = ubi32_uarttio_release_port, ++ .request_port = ubi32_uarttio_request_port, ++ .config_port = ubi32_uarttio_config_port, ++ .verify_port = ubi32_uarttio_verify_port, ++}; ++ ++/* ++ * ubi32_uarttio_add_ports ++ */ ++static int __init ubi32_uarttio_add_ports(void) ++{ ++ int res = 0; ++ struct ubi32_uarttio_port *uup = uarttio_ports; ++ int i = 0; ++ ++ for (i = 0; i < uarttio_nports; i++) { ++ /* ++ * Setup the GPIOs ++ */ ++ res = gpio_request(uup->tx_pin, "ubi32_uarttio_tx"); ++ if (res) { ++ printk(KERN_WARNING "Failed to request GPIO %d\n", uup->tx_pin); ++ res = -EBUSY; ++ goto next; ++ } ++ ++ res = gpio_request(uup->rx_pin, "ubi32_uarttio_rx"); ++ if (res) { ++ gpio_free(uup->tx_pin); ++ printk(KERN_WARNING "Failed to request GPIO %d\n", uup->rx_pin); ++ res = -EBUSY; ++ goto next; ++ } ++ ++ res = uart_add_one_port(&ubi32_uarttio_uart_driver, &uup->port); ++ if (res) { ++ gpio_free(uup->rx_pin); ++ gpio_free(uup->tx_pin); ++ res = -ENODEV; ++ printk(KERN_WARNING "Failed to add port %d,%d\n", uup->tx_pin, uup->rx_pin); ++ goto next; ++ } ++ uup->added = 1; ++ ++ /* ++ * Set the direction of the ports now, after we're sure that everything is ok ++ */ ++ if (!uup->port_init) { ++ gpio_direction_output(uup->tx_pin, 1); ++ gpio_direction_input(uup->rx_pin); ++ } ++ ++next: ++ uup++; ++ } ++ return res; ++} ++ ++/* ++ * ubi32_uarttio_cleanup ++ */ ++static void ubi32_uarttio_cleanup(void) ++{ ++ struct ubi32_uarttio_port *uup; ++ int i; ++ ++ /* ++ * Stop the hardware thread ++ */ ++ if (uarttio_inst.regs) { ++ thread_disable(uarttio_inst.regs->thread); ++ } ++ if (uarttio_inst.irq_requested) { ++ free_irq(uarttio_inst.irq, NULL); ++ } ++ ++ /* ++ * Get rid of the ports ++ */ ++ uup = uarttio_inst.ports; ++ for (i = 0; i < uarttio_nports; i++) { ++ gpio_free(uup->tx_pin); ++ gpio_free(uup->rx_pin); ++ if (uup->added) { ++ uart_remove_one_port(&ubi32_uarttio_uart_driver, &uup->port); ++ } ++ uup++; ++ } ++ ++ if (uarttio_inst.driver_registered) { ++ uart_unregister_driver(&ubi32_uarttio_uart_driver); ++ } ++} ++ ++/* ++ * ubi32_uarttio_setup_port ++ * Setup a port in the TIO registers ++ */ ++static int ubi32_uarttio_setup_port(int index, ++ struct uarttio_uart *uart, ++ unsigned int baud, unsigned int tx_pin, ++ unsigned int rx_pin) ++{ ++ struct ubi32_uarttio_port *uup = &uarttio_ports[index]; ++ void *tx_port = ubi_gpio_get_port(tx_pin); ++ void *rx_port = ubi_gpio_get_port(rx_pin); ++ ++ /* ++ * Verify the ports are on chip ++ */ ++ if (!tx_port || !rx_port) { ++ printk(KERN_WARNING "Invalid port(s) specified: %u or %u\n", tx_pin, rx_pin); ++ return -EINVAL; ++ } ++ ++ uup->tx_pin = tx_pin; ++ uup->rx_pin = rx_pin; ++ uup->uart = uart; ++ ++ /* ++ * Setup the port structure ++ */ ++ uup->port.ops = &ubi32_uarttio_pops; ++ uup->port.line = index; ++ uup->port.iotype = UPIO_MEM; ++ uup->port.flags = UPF_BOOT_AUTOCONF; ++ uup->port.fifosize = uup->uart->tx_fifo_size; ++ uup->port.private_data = uup; ++ ++ /* ++ * We share this IRQ across all ports ++ */ ++ uup->port.irq = uarttio_inst.irq; ++ ++ /* ++ * We really don't have a mem/map base but without these variables ++ * set, the serial_core won't startup. ++ */ ++ uup->port.membase = (void __iomem *)uup; ++ uup->port.mapbase = (resource_size_t)uup; ++ spin_lock_init(&uup->port.lock); ++ ++ /* ++ * Set up the hardware ++ */ ++ uart->flags = UARTTIO_UART_FLAG_SET_RATE | UARTTIO_UART_FLAG_RESET; ++ ++ uart->tx_port = (unsigned int)tx_port; ++ uart->tx_pin = gpio_pin_index(tx_pin); ++ uart->tx_bits = 8; ++ uart->tx_stop_bits = 1; ++ ++ uart->rx_port = (unsigned int)rx_port; ++ uart->rx_pin = gpio_pin_index(rx_pin); ++ uart->rx_bits = 8; ++ uart->rx_stop_bits = 1; ++ ++ uart->baud_rate = baud; ++ ++ return 0; ++} ++ ++enum ubi32_uarttio_parse_states { ++ UBI32_UARTTIO_PARSE_STATE_BAUD, ++ UBI32_UARTTIO_PARSE_STATE_TX_PIN, ++ UBI32_UARTTIO_PARSE_STATE_RX_PIN, ++ UBI32_UARTTIO_PARSE_STATE_HS, ++ UBI32_UARTTIO_PARSE_STATE_CTS_PIN, ++ UBI32_UARTTIO_PARSE_STATE_RTS_PIN, ++}; ++ ++/* ++ * ubi32_uarttio_parse_param ++ */ ++static int ubi32_uarttio_parse_param(char *str) ++{ ++ int res; ++ int i; ++ int baud = 0; ++ int tx_pin = 0; ++ int rx_pin = 0; ++ int hs = 0; ++ int cts_pin = 0; ++ int rts_pin = 0; ++ int nfound = 0; ++ enum ubi32_uarttio_parse_states state = UBI32_UARTTIO_PARSE_STATE_BAUD; ++ struct uarttio_uart *uart = uarttio_inst.regs->uarts; ++ ++ /* ++ * Run though the options and generate the proper structures ++ */ ++ res = get_option(&str, &i); ++ while ((res == 2) || (res == 1)) { ++ switch (state) { ++ case UBI32_UARTTIO_PARSE_STATE_BAUD: ++ /* ++ * If we are here and nfound > 0 then create the port ++ * based on the previous input ++ */ ++ if (nfound) { ++ /* ++ * Create the port ++ */ ++ if (ubi32_uarttio_setup_port(nfound - 1, uart, baud, tx_pin, rx_pin)) { ++ /* ++ * Port was invalid ++ */ ++ goto fail; ++ } else { ++ printk(KERN_INFO "Serial port %d: tx=%d:rx=%d @ %d\n", nfound, tx_pin, rx_pin, baud); ++ uart++; ++ } ++ } ++ ++ /* ++ * Reset the variables and go to the next state ++ */ ++ hs = 0; ++ baud = i; ++ state = UBI32_UARTTIO_PARSE_STATE_TX_PIN; ++ break; ++ ++ case UBI32_UARTTIO_PARSE_STATE_TX_PIN: ++ tx_pin = i; ++ state = UBI32_UARTTIO_PARSE_STATE_RX_PIN; ++ break; ++ ++ case UBI32_UARTTIO_PARSE_STATE_RX_PIN: ++ rx_pin = i; ++ state = UBI32_UARTTIO_PARSE_STATE_HS; ++ break; ++ ++ case UBI32_UARTTIO_PARSE_STATE_HS: ++ hs = i; ++ if (hs) { ++ state = UBI32_UARTTIO_PARSE_STATE_CTS_PIN; ++ break; ++ } ++ ++ if (nfound == uarttio_inst.regs->max_uarts) { ++ printk(KERN_WARNING "Maximum number of serial ports reached\n"); ++ goto done; ++ } ++ nfound++; ++ state = UBI32_UARTTIO_PARSE_STATE_BAUD; ++ break; ++ ++ case UBI32_UARTTIO_PARSE_STATE_CTS_PIN: ++ cts_pin = i; ++ state = UBI32_UARTTIO_PARSE_STATE_RTS_PIN; ++ break; ++ ++ case UBI32_UARTTIO_PARSE_STATE_RTS_PIN: ++ rts_pin = i; ++ ++ if (nfound == uarttio_inst.regs->max_uarts) { ++ printk(KERN_WARNING "Maximum number of serial ports reached\n"); ++ goto done; ++ } ++ nfound++; ++ state = UBI32_UARTTIO_PARSE_STATE_BAUD; ++ break; ++ } ++ res = get_option(&str, &i); ++ } ++ ++ if ((res > 2) || state != UBI32_UARTTIO_PARSE_STATE_BAUD) { ++ printk(KERN_WARNING "Parameter syntax error.\n"); ++ res = -EINVAL; ++ goto fail; ++ } ++ ++ /* ++ * Create the final port ++ */ ++ if (ubi32_uarttio_setup_port(nfound - 1, uart, baud, tx_pin, rx_pin)) { ++ goto fail; ++ } ++ printk(KERN_INFO "Serial port %d: tx=%d:rx=%d @ %d\n", nfound, tx_pin, rx_pin, baud); ++ ++done: ++ uarttio_nports = nfound; ++ ++ return nfound ? 0 : -ENODEV; ++ ++fail: ++ /* ++ * Reset the ports ++ */ ++ uart = uarttio_inst.regs->uarts; ++ for (i = 0; i < uarttio_inst.regs->max_uarts; i++) { ++ uart->flags = 0; ++ uart++; ++ } ++ ++ return res; ++} ++ ++/* ++ * ubi32_uarttio_probe ++ */ ++static int ubi32_uarttio_probe(void) ++{ ++ int ret; ++ struct uarttio_node *uart_node; ++ char *str = utio_ports_param; ++ static int probed; ++ ++ /* ++ * We only want to be probed once, we could be probed twice ++ * for example if we are used as a console ++ */ ++ if (probed) { ++ return 0; ++ } ++ probed = 1; ++ ++ /* ++ * Extract the TIO name from the setup string ++ */ ++ while (*str) { ++ if (*str == ',') { ++ *str++ = 0; ++ break; ++ } ++ str++; ++ } ++ ++ if (!*str) { ++ return -EINVAL; ++ } ++ ++ uart_node = (struct uarttio_node *)devtree_find_node(utio_ports_param); ++ if (!uart_node) { ++ return -ENODEV; ++ } ++ ++ uarttio_inst.irq = uart_node->dn.recvirq; ++ uarttio_inst.regs = uart_node->regs; ++ ++ /* ++ * Parse module parameters. ++ */ ++ ret = ubi32_uarttio_parse_param(str); ++ if (ret != 0) { ++ goto fail; ++ } ++ ++ ubi32_uarttio_uart_driver.nr = uarttio_nports; ++ ++ return 0; ++ ++fail: ++ ubi32_uarttio_cleanup(); ++ return ret; ++} ++ ++#if defined(CONFIG_SERIAL_UBI32_UARTTIO_CONSOLE) ++/* ++ * ubi32_uarttio_console_setup ++ */ ++static int __init ubi32_uarttio_console_setup(struct console *co, char *options) ++{ ++ int baud; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ struct ubi32_uarttio_port *uup; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ if (co->index == -1 || co->index >= uarttio_nports) { ++ co->index = 0; ++ } ++ uup = &uarttio_ports[co->index]; ++ baud = uup->uart->baud_rate; ++ uup->uart->flags |= UARTTIO_UART_FLAG_ENABLED; ++ ++ /* ++ * Setup the GPIOs ++ * We have to use the direct interface because the gpio ++ * subsystem is not available at this point. ++ */ ++ uup->port_init = 1; ++ UBICOM32_GPIO_SET_PIN_HIGH(uup->tx_pin); ++ UBICOM32_GPIO_SET_PIN_OUTPUT(uup->tx_pin); ++ UBICOM32_GPIO_SET_PIN_INPUT(uup->rx_pin); ++ ++ /* ++ * Start the thread ++ */ ++ thread_enable(uarttio_inst.regs->thread); ++ ++ /* ++ * Process options ++ */ ++ if (options) { ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ if (ubi32_uarttio_set_baud(uup, baud)) { ++ baud = uup->uart->current_baud_rate; ++ } ++ } ++ ++ return uart_set_options(&uup->port, co, baud, 'n', 8, 'n'); ++} ++ ++/* ++ * ubi32_uarttio_console_putchar ++ */ ++static void ubi32_uarttio_console_putchar(struct uart_port *port, int ch) ++{ ++ struct ubi32_uarttio_port *uup = port->private_data; ++ ++ while (ubi32_uarttio_put_char(uup->uart, ch)) { ++ cpu_relax(); ++ } ++} ++ ++/* ++ * ubi32_uarttio_console_write ++ * Interrupts are disabled on entering ++ */ ++static void ubi32_uarttio_console_write(struct console *co, const char *s, unsigned int count) ++{ ++ struct uart_port *port = &(uarttio_ports[co->index].port); ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ uart_console_write(port, s, count, ubi32_uarttio_console_putchar); ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static struct console ubi32_uarttio_console = { ++ .name = UBI32_UARTTIO_NAME, ++ .write = ubi32_uarttio_console_write, ++ .device = uart_console_device, ++ .setup = ubi32_uarttio_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++ .data = &ubi32_uarttio_uart_driver, ++}; ++ ++static int __init ubi32_uarttio_console_init(void) ++{ ++ int res; ++ ++ res = ubi32_uarttio_probe(); ++ if (res) { ++ return res; ++ } ++ ++ register_console(&ubi32_uarttio_console); ++ return 0; ++} ++console_initcall(ubi32_uarttio_console_init); ++#endif /* CONFIG_SERIAL_UBI32_UARTTIO_CONSOLE */ ++ ++/* ++ * ubi32_serial_suspend ++ */ ++static int ubi32_uarttio_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ int i; ++ for (i = 0; i < uarttio_nports; i++) { ++ uart_suspend_port(&ubi32_uarttio_uart_driver, &uarttio_ports[i].port); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ubi32_serial_resume ++ */ ++static int ubi32_uarttio_resume(struct platform_device *pdev) ++{ ++ int i; ++ for (i = 0; i < uarttio_nports; i++) { ++ uart_resume_port(&ubi32_uarttio_uart_driver, &uarttio_ports[i].port); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ubi32_uarttio_remove ++ */ ++static int __devexit ubi32_uarttio_remove(struct platform_device *pdev) ++{ ++ ubi32_uarttio_cleanup(); ++ ++ uart_unregister_driver(&ubi32_uarttio_uart_driver); ++ ++ return 0; ++} ++ ++static struct platform_driver ubi32_uarttio_platform_driver = { ++ .remove = __devexit_p(ubi32_uarttio_remove), ++ .suspend = ubi32_uarttio_suspend, ++ .resume = ubi32_uarttio_resume, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++#ifndef MODULE ++/* ++ * Called at boot time. ++ * ++ * uarttio=TIONAME,(baud,tx_pin,rx_pin,handshake[,cts_pin,rts_pin],...) ++ * TIONAME is the name of the devtree node which describes the UARTTIO ++ * pin is the index of the pin, i.e. PA4 is 5 [(port * 32) + pin] ++ * handshake = 1 to enable handshaking, provide cts_pin, rts_pin (UNSUPPORTED) ++ * handshake = 0 to disable handshaking, do not provide cts_pin, rts_pin ++ * Ex: uarttio=UARTTIO,57600,7,6,0,9600,8,9,0 ++ */ ++static int __init ubi32_uarttio_setup(char *str) ++{ ++ strncpy(utio_ports_param, str, UBI32_UARTTIO_MAX_PARAM_LEN); ++ utio_ports_param[UBI32_UARTTIO_MAX_PARAM_LEN - 1] = 0; ++ return 1; ++} ++__setup("uarttio=", ubi32_uarttio_setup); ++#endif ++ ++/* ++ * ubi32_uarttio_init ++ */ ++static int __init ubi32_uarttio_init(void) ++{ ++ int ret; ++ int i; ++ ++ ret = ubi32_uarttio_probe(); ++ if (ret) { ++ return ret; ++ } ++ ++ /* ++ * Request the IRQ (do it here since many ports share the same IRQ) ++ */ ++ ret = request_irq(uarttio_inst.irq, ubi32_uarttio_isr, IRQF_DISABLED, DRIVER_NAME, NULL); ++ if (ret != 0) { ++ printk(KERN_WARNING "Could not request IRQ %d\n", uarttio_inst.irq); ++ goto fail; ++ } ++ uarttio_inst.irq_requested = 1; ++ ++ /* ++ * Register the UART driver and add the ports ++ */ ++ ret = uart_register_driver(&ubi32_uarttio_uart_driver); ++ if (ret != 0) { ++ goto fail; ++ } ++ uarttio_inst.driver_registered = 1; ++ ++ ret = ubi32_uarttio_add_ports(); ++ if (ret != 0) { ++ ubi32_uarttio_cleanup(); ++ return ret; ++ } ++ ++ /* ++ * Start the thread ++ */ ++ thread_enable(uarttio_inst.regs->thread); ++ ++ for (i = 0; i < uarttio_nports; i++) { ++ pr_info("Serial: Ubicom32 uarttio #%d: tx:%d rx:%d baud:%d\n", ++ i, uarttio_ports[i].tx_pin, uarttio_ports[i].rx_pin, ++ uarttio_ports[i].uart->current_baud_rate); ++ } ++ pr_info("Serial: Ubicom32 uarttio started on thread:%d irq:%d\n", uarttio_inst.regs->thread, uarttio_inst.irq); ++ ++ return ret; ++ ++fail: ++ ubi32_uarttio_cleanup(); ++ return ret; ++} ++module_init(ubi32_uarttio_init); ++ ++/* ++ * ubi32_uarttio_exit ++ */ ++static void __exit ubi32_uarttio_exit(void) ++{ ++ platform_driver_unregister(&ubi32_uarttio_platform_driver); ++} ++module_exit(ubi32_uarttio_exit); ++ ++module_param_string(ports, utio_ports_param, sizeof(utio_ports_param), 0444); ++MODULE_PARM_DESC(ports, "Sets the ports to allocate: ports=TIONAME,(baud,txpin,rxpin,handshake[,ctspin,rtspin],...)\n" ++ " TIONAME is the name of the devtree node which describes the UARTTIO\n" ++ " pin is the index of the pin, i.e. PA4 is 5 [(port * 32) + pin]\n" ++ " handshake = 1 to enable handshaking, provide ctspin, rtspin (UNSUPPORTED)\n" ++ " handshake = 0 to disable handshaking, do not provide ctspin, rtspin\n" ++ " Ex: ports=UARTTIO,57600,7,6,0,9600,8,9,0\n"); ++MODULE_AUTHOR("Patrick Tjin <pat.tjin@ubicom.com>"); ++MODULE_DESCRIPTION("Ubicom serial virtual peripherial driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_CHARDEV_MAJOR(UBI32_UARTTIO_MAJOR); ++MODULE_ALIAS("platform:" DRIVER_NAME); +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -196,6 +196,15 @@ config SPI_S3C24XX + help + SPI driver for Samsung S3C24XX series ARM SoCs + ++config SPI_UBICOM32_GPIO ++ tristate "Ubicom32 SPI over GPIO" ++ depends on SPI_MASTER && UBICOM32 && EXPERIMENTAL ++ select SPI_BITBANG ++ select HAS_DMA ++ help ++ SPI driver for the Ubicom32 architecture using ++ GPIO lines to provide the SPI bus. ++ + config SPI_S3C24XX_GPIO + tristate "Samsung S3C24XX series SPI by GPIO" + depends on ARCH_S3C2410 && EXPERIMENTAL +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_ORION) += orion_spi.o + obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o + obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o + obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o ++obj-$(CONFIG_SPI_UBICOM32_GPIO) += spi_ubicom32_gpio.o + obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o + obj-$(CONFIG_SPI_TXX9) += spi_txx9.o + obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o +--- /dev/null ++++ b/drivers/spi/spi_ubicom32_gpio.c +@@ -0,0 +1,268 @@ ++/* ++ * drivers/spi_spi_ubicom32_gpio.c ++ * Ubicom32 GPIO based SPI driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++#include <linux/platform_device.h> ++ ++#include <linux/spi/spi.h> ++#include <linux/spi/spi_bitbang.h> ++ ++#include <linux/gpio.h> ++ ++#include <asm/ip5000.h> ++#include <asm/ubicom32-spi-gpio.h> ++ ++#define DRIVER_NAME "ubicom32-spi-gpio" ++ ++struct ubicom32_spi_gpio { ++ struct spi_bitbang bitbang; ++ ++ struct ubicom32_spi_gpio_platform_data *pdata; ++ ++ struct platform_device *dev; ++}; ++ ++/* ++ * The following 4 functions are used by EXPAND_BITBANG_TXRX to bitbang the data out. ++ */ ++static inline void setsck(struct spi_device *dev, int on) ++{ ++ struct ubicom32_spi_gpio *usg = (struct ubicom32_spi_gpio *)spi_master_get_devdata(dev->master); ++ gpio_set_value(usg->pdata->pin_clk, on ? 1 : 0); ++} ++ ++static inline void setmosi(struct spi_device *dev, int on) ++{ ++ struct ubicom32_spi_gpio *usg = (struct ubicom32_spi_gpio *)spi_master_get_devdata(dev->master); ++ gpio_set_value(usg->pdata->pin_mosi, on ? 1 : 0); ++} ++ ++static inline u32 getmiso(struct spi_device *dev) ++{ ++ struct ubicom32_spi_gpio *usg = (struct ubicom32_spi_gpio *)spi_master_get_devdata(dev->master); ++ return gpio_get_value(usg->pdata->pin_miso) ? 1 : 0; ++} ++ ++#define spidelay(x) ndelay(x) ++ ++#define EXPAND_BITBANG_TXRX ++#include <linux/spi/spi_bitbang.h> ++ ++/* ++ * ubicom32_spi_gpio_txrx_mode0 ++ */ ++static u32 ubicom32_spi_gpio_txrx_mode0(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) ++{ ++ return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); ++} ++ ++/* ++ * ubicom32_spi_gpio_txrx_mode1 ++ */ ++static u32 ubicom32_spi_gpio_txrx_mode1(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) ++{ ++ return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); ++} ++ ++/* ++ * ubicom32_spi_gpio_txrx_mode2 ++ */ ++static u32 ubicom32_spi_gpio_txrx_mode2(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) ++{ ++ return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); ++} ++ ++/* ++ * ubicom32_spi_gpio_txrx_mode3 ++ */ ++static u32 ubicom32_spi_gpio_txrx_mode3(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) ++{ ++ return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); ++} ++ ++/* ++ * ubicom32_spi_gpio_chipselect ++ */ ++static void ubicom32_spi_gpio_chipselect(struct spi_device *dev, int value) ++{ ++ struct ubicom32_spi_gpio_controller_data *cd = (struct ubicom32_spi_gpio_controller_data *)dev->controller_data; ++ unsigned int cs_polarity = dev->mode & SPI_CS_HIGH ? 1 : 0; ++ ++ if (value == BITBANG_CS_ACTIVE) { ++ gpio_set_value(cd->pin_cs, cs_polarity); ++ return; ++ } ++ gpio_set_value(cd->pin_cs, !cs_polarity); ++} ++ ++/* ++ * ubicom32_spi_gpio_probe ++ */ ++static int ubicom32_spi_gpio_probe(struct platform_device *dev) ++{ ++ struct ubicom32_spi_gpio_platform_data *pdata; ++ struct spi_master *master; ++ struct ubicom32_spi_gpio *usg; ++ int ret; ++ ++ master = spi_alloc_master(&dev->dev, sizeof(struct ubicom32_spi_gpio)); ++ if (master == NULL) { ++ dev_err(&dev->dev, "failed to allocate spi master\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ usg = (struct ubicom32_spi_gpio *)spi_master_get_devdata(master); ++ ++ platform_set_drvdata(dev, usg); ++ ++ /* ++ * Copy in the platform data ++ */ ++ pdata = dev->dev.platform_data; ++ usg->pdata = dev->dev.platform_data; ++ ++ /* ++ * Request the GPIO lines ++ */ ++ ret = gpio_request(pdata->pin_mosi, "spi-mosi"); ++ if (ret) { ++ dev_err(&dev->dev, "Failed to allocate spi-mosi GPIO\n"); ++ goto err; ++ } ++ ++ ret = gpio_request(pdata->pin_miso, "spi-miso"); ++ if (ret) { ++ dev_err(&dev->dev, "Failed to allocate spi-miso GPIO\n"); ++ goto err_nomiso; ++ } ++ ++ ret = gpio_request(pdata->pin_clk, "spi-clk"); ++ if (ret) { ++ dev_err(&dev->dev, "Failed to allocate spi-clk GPIO\n"); ++ goto err_noclk; ++ } ++ ++ /* ++ * Setup spi-bitbang adaptor ++ */ ++ usg->bitbang.flags |= SPI_CS_HIGH; ++ usg->bitbang.master = spi_master_get(master); ++ usg->bitbang.master->bus_num = pdata->bus_num; ++ usg->bitbang.master->num_chipselect = pdata->num_chipselect; ++ usg->bitbang.chipselect = ubicom32_spi_gpio_chipselect; ++ ++ usg->bitbang.txrx_word[SPI_MODE_0] = ubicom32_spi_gpio_txrx_mode0; ++ usg->bitbang.txrx_word[SPI_MODE_1] = ubicom32_spi_gpio_txrx_mode1; ++ usg->bitbang.txrx_word[SPI_MODE_2] = ubicom32_spi_gpio_txrx_mode2; ++ usg->bitbang.txrx_word[SPI_MODE_3] = ubicom32_spi_gpio_txrx_mode3; ++ ++ /* ++ * Setup the GPIO pins ++ */ ++ gpio_direction_output(pdata->pin_clk, pdata->clk_default); ++ gpio_direction_output(pdata->pin_mosi, 0); ++ gpio_direction_input(pdata->pin_miso); ++ ++ /* ++ * Ready to go ++ */ ++ ret = spi_bitbang_start(&usg->bitbang); ++ if (ret) { ++ goto err_no_bitbang; ++ } ++ ++ return 0; ++ ++err_no_bitbang: ++ spi_master_put(usg->bitbang.master); ++ ++ gpio_free(pdata->pin_clk); ++ ++err_noclk: ++ gpio_free(pdata->pin_miso); ++ ++err_nomiso: ++ gpio_free(pdata->pin_mosi); ++ ++err: ++ return ret; ++} ++ ++/* ++ * ubicom32_spi_gpio_remove ++ */ ++static int ubicom32_spi_gpio_remove(struct platform_device *dev) ++{ ++ struct ubicom32_spi_gpio *sp = platform_get_drvdata(dev); ++ ++ spi_bitbang_stop(&sp->bitbang); ++ spi_master_put(sp->bitbang.master); ++ ++ return 0; ++} ++ ++/* ++ * Work with hotplug and coldplug ++ */ ++MODULE_ALIAS("platform:ubicom32_spi_gpio"); ++ ++static struct platform_driver ubicom32_spi_gpio_drv = { ++ .probe = ubicom32_spi_gpio_probe, ++ .remove = ubicom32_spi_gpio_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/* ++ * ubicom32_spi_gpio_init ++ */ ++static int __init ubicom32_spi_gpio_init(void) ++{ ++ return platform_driver_register(&ubicom32_spi_gpio_drv); ++} ++ ++/* ++ * ubicom32_spi_gpio_exit ++ */ ++static void __exit ubicom32_spi_gpio_exit(void) ++{ ++ platform_driver_unregister(&ubicom32_spi_gpio_drv); ++} ++ ++module_init(ubicom32_spi_gpio_init); ++module_exit(ubicom32_spi_gpio_exit); ++ ++MODULE_DESCRIPTION("Ubicom32 SPI-GPIO Driver"); ++MODULE_AUTHOR("Pat Tjin, <@ubicom.com>"); ++MODULE_LICENSE("GPL"); +--- a/drivers/uio/Kconfig ++++ b/drivers/uio/Kconfig +@@ -71,4 +71,12 @@ config UIO_SERCOS3 + + If you compile this as a module, it will be called uio_sercos3. + ++config UIO_UBICOM32RING ++ tristate "Ubicom32 Ring Buffer driver" ++ default n ++ help ++ Userspace I/O interface for a Ubicom32 Ring Buffer. ++ ++ If you compile this as a module, it will be called uio_ubicom32ring ++ + endif +--- a/drivers/uio/Makefile ++++ b/drivers/uio/Makefile +@@ -4,3 +4,4 @@ obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o + obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o + obj-$(CONFIG_UIO_SMX) += uio_smx.o + obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o ++obj-$(CONFIG_UIO_UBICOM32RING) += uio_ubicom32ring.o +--- /dev/null ++++ b/drivers/uio/uio_ubicom32ring.c +@@ -0,0 +1,289 @@ ++/* ++ * drivers/uio/uio_ubicom32ring.c ++ * ++ * Userspace I/O platform driver for Ubicom32 ring buffers ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * Based on uio_ubicom32ring.c by Magnus Damm ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/uio_driver.h> ++#include <linux/spinlock.h> ++#include <linux/bitops.h> ++#include <linux/interrupt.h> ++#include <linux/stringify.h> ++ ++#include <asm/ip5000.h> ++#include <asm/ubicom32ring.h> ++ ++#define DRIVER_NAME "uio_ubicom32ring" ++ ++struct uio_ubicom32ring_data { ++ struct uio_info *uioinfo; ++ ++ struct uio_ubicom32ring_regs *regs; ++ ++ /* ++ * IRQ used to kick the ring buffer ++ */ ++ int irq_tx; ++ int irq_rx; ++ ++ spinlock_t lock; ++ ++ unsigned long flags; ++ ++ char name[0]; ++}; ++ ++static irqreturn_t uio_ubicom32ring_handler(int irq, struct uio_info *dev_info) ++{ ++ struct uio_ubicom32ring_data *priv = dev_info->priv; ++ ++ /* Just disable the interrupt in the interrupt controller, and ++ * remember the state so we can allow user space to enable it later. ++ */ ++ ++ if (!test_and_set_bit(0, &priv->flags)) ++ disable_irq_nosync(irq); ++ ++ return IRQ_HANDLED; ++} ++ ++static int uio_ubicom32ring_irqcontrol(struct uio_info *dev_info, s32 irq_on) ++{ ++ struct uio_ubicom32ring_data *priv = dev_info->priv; ++ unsigned long flags; ++ ++ /* Allow user space to enable and disable the interrupt ++ * in the interrupt controller, but keep track of the ++ * state to prevent per-irq depth damage. ++ * ++ * Serialize this operation to support multiple tasks. ++ */ ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ ++ if (irq_on & 2) { ++ /* ++ * Kick the ring buffer (if we can) ++ */ ++ if (priv->irq_tx != 0xFF) { ++ ubicom32_set_interrupt(priv->irq_tx); ++ } ++ } ++ ++ if (priv->irq_rx != 0xFF) { ++ if (irq_on & 1) { ++ if (test_and_clear_bit(0, &priv->flags)) ++ enable_irq(dev_info->irq); ++ } else { ++ if (!test_and_set_bit(0, &priv->flags)) ++ disable_irq(dev_info->irq); ++ } ++ } ++ ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ return 0; ++} ++ ++static int uio_ubicom32ring_probe(struct platform_device *pdev) ++{ ++ struct uio_info *uioinfo; ++ struct uio_mem *uiomem; ++ struct uio_ubicom32ring_data *priv; ++ struct uio_ubicom32ring_regs *regs; ++ struct resource *mem_resource; ++ struct resource *irqtx_resource; ++ struct resource *irqrx_resource; ++ int ret = -EINVAL; ++ int i; ++ ++ uioinfo = kzalloc(sizeof(struct uio_info), GFP_KERNEL); ++ if (!uioinfo) { ++ dev_err(&pdev->dev, "unable to kmalloc\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * Allocate private data with some string space after ++ */ ++ i = sizeof(DRIVER_NAME) + 1; ++ i += pdev->dev.platform_data ? strlen(pdev->dev.platform_data) : 0; ++ priv = kzalloc(sizeof(struct uio_ubicom32ring_data) + i, GFP_KERNEL); ++ if (!priv) { ++ dev_err(&pdev->dev, "unable to kmalloc\n"); ++ kfree(uioinfo); ++ return -ENOMEM; ++ } ++ ++ strcpy(priv->name, DRIVER_NAME ":"); ++ if (pdev->dev.platform_data) { ++ strcat(priv->name, pdev->dev.platform_data); ++ } ++ uioinfo->priv = priv; ++ uioinfo->name = priv->name; ++ uioinfo->version = "0.1"; ++ ++ priv->uioinfo = uioinfo; ++ spin_lock_init(&priv->lock); ++ priv->flags = 0; /* interrupt is enabled to begin with */ ++ ++ /* ++ * Get our resources, the IRQ_TX and IRQ_RX are optional. ++ */ ++ priv->irq_tx = 0xFF; ++ irqtx_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (irqtx_resource) { ++ priv->irq_tx = irqtx_resource->start; ++ } ++ ++ uioinfo->irq = -1; ++ priv->irq_rx = 0xFF; ++ irqrx_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 1); ++ if (irqrx_resource) { ++ priv->irq_rx = irqrx_resource->start; ++ uioinfo->irq = priv->irq_rx; ++ uioinfo->handler = uio_ubicom32ring_handler; ++ } ++ ++ mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!mem_resource || !mem_resource->start) { ++ dev_err(&pdev->dev, "No valid memory resource found\n"); ++ ret = -ENODEV; ++ goto fail; ++ } ++ regs = (struct uio_ubicom32ring_regs *)mem_resource->start; ++ priv->regs = regs; ++ ++ if (regs->version != UIO_UBICOM32RING_REG_VERSION) { ++ dev_err(&pdev->dev, "version %d not supported\n", regs->version); ++ ret = -ENODEV; ++ goto fail; ++ } ++ ++ /* ++ * First range is the shared register space, if we have any ++ */ ++ uiomem = &uioinfo->mem[0]; ++ if (regs->regs_size) { ++ uiomem->memtype = UIO_MEM_PHYS; ++ uiomem->addr = (u32_t)regs->regs; ++ uiomem->size = regs->regs_size; ++ ++uiomem; ++ dev_info(&pdev->dev, "regs:%p (%u) / rings: %d found\n", regs->regs, regs->regs_size, regs->num_rings); ++ } else { ++ dev_info(&pdev->dev, "rings: %d found\n", regs->num_rings); ++ } ++ ++ /* ++ * The rest of the range correspond to the rings ++ */ ++ for (i = 0; i < regs->num_rings; i++) { ++ dev_info(&pdev->dev, "\t%d: entries:%d ring:%p\n", ++ i, regs->rings[i]->entries, &(regs->rings[i]->ring)); ++ if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) { ++ dev_warn(&pdev->dev, "device has more than " ++ __stringify(MAX_UIO_MAPS) ++ " I/O memory resources.\n"); ++ break; ++ } ++ ++ uiomem->memtype = UIO_MEM_PHYS; ++ uiomem->addr = (u32_t)&(regs->rings[i]->head); ++ uiomem->size = (regs->rings[i]->entries * sizeof(u32_t)) + ++ sizeof(struct uio_ubicom32ring_desc); ++ ++uiomem; ++ } ++ ++ while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) { ++ uiomem->size = 0; ++ ++uiomem; ++ } ++ ++ /* This driver requires no hardware specific kernel code to handle ++ * interrupts. Instead, the interrupt handler simply disables the ++ * interrupt in the interrupt controller. User space is responsible ++ * for performing hardware specific acknowledge and re-enabling of ++ * the interrupt in the interrupt controller. ++ * ++ * Interrupt sharing is not supported. ++ */ ++ uioinfo->irq_flags = IRQF_DISABLED; ++ uioinfo->irqcontrol = uio_ubicom32ring_irqcontrol; ++ ++ ret = uio_register_device(&pdev->dev, priv->uioinfo); ++ if (ret) { ++ dev_err(&pdev->dev, "unable to register uio device\n"); ++ goto fail; ++ } ++ ++ platform_set_drvdata(pdev, priv); ++ ++ dev_info(&pdev->dev, "'%s' using irq: rx %d tx %d, regs %p\n", ++ priv->name, priv->irq_rx, priv->irq_tx, priv->regs); ++ ++ return 0; ++ ++fail: ++ kfree(uioinfo); ++ kfree(priv); ++ return ret; ++} ++ ++static int uio_ubicom32ring_remove(struct platform_device *pdev) ++{ ++ struct uio_ubicom32ring_data *priv = platform_get_drvdata(pdev); ++ ++ uio_unregister_device(priv->uioinfo); ++ kfree(priv->uioinfo); ++ kfree(priv); ++ return 0; ++} ++ ++static struct platform_driver uio_ubicom32ring = { ++ .probe = uio_ubicom32ring_probe, ++ .remove = uio_ubicom32ring_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init uio_ubicom32ring_init(void) ++{ ++ return platform_driver_register(&uio_ubicom32ring); ++} ++ ++static void __exit uio_ubicom32ring_exit(void) ++{ ++ platform_driver_unregister(&uio_ubicom32ring); ++} ++ ++module_init(uio_ubicom32ring_init); ++module_exit(uio_ubicom32ring_exit); ++ ++MODULE_AUTHOR("Patrick Tjin"); ++MODULE_DESCRIPTION("Userspace I/O driver for Ubicom32 ring buffers"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" DRIVER_NAME); ++ +--- a/drivers/usb/gadget/epautoconf.c ++++ b/drivers/usb/gadget/epautoconf.c +@@ -154,6 +154,10 @@ ep_matches ( + /* configure your hardware with enough buffering!! */ + } + break; ++ ++ case USB_ENDPOINT_XFER_BULK: ++ if ((gadget->is_dualspeed) && (ep->maxpacket < 512)) ++ return 0; + } + + /* MATCH!! */ +@@ -174,7 +178,7 @@ ep_matches ( + return 0; + desc->bEndpointAddress |= epnum; + } +- ++ + /* report (variable) full speed bulk maxpacket */ + if (USB_ENDPOINT_XFER_BULK == type) { + int size = ep->maxpacket; +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -22,6 +22,7 @@ config USB_ARCH_HAS_HCD + default y if PCMCIA && !M32R # sl811_cs + default y if ARM # SL-811 + default y if SUPERH # r8a66597-hcd ++ default y if UBICOM32 # Ubicom's onchip USB Duial role controller + default PCI + + # many non-PCI SOC chips embed OHCI +--- a/drivers/usb/musb/Kconfig ++++ b/drivers/usb/musb/Kconfig +@@ -11,7 +11,7 @@ config USB_MUSB_HDRC + depends on (USB || USB_GADGET) && HAVE_CLK + depends on !SUPERH + select TWL4030_USB if MACH_OMAP_3430SDP +- tristate 'Inventra Highspeed Dual Role Controller (TI, ...)' ++ tristate 'Inventra Highspeed Dual Role Controller (TI, Ubicom, ...)' + help + Say Y here if your system has a dual role high speed USB + controller based on the Mentor Graphics silicon IP. Then +--- a/drivers/usb/musb/Makefile ++++ b/drivers/usb/musb/Makefile +@@ -22,6 +22,10 @@ ifeq ($(CONFIG_ARCH_OMAP3430),y) + musb_hdrc-objs += omap2430.o + endif + ++ifeq ($(CONFIG_UBICOM32), y) ++ musb_hdrc-objs += ubi32_usb.o ++endif ++ + ifeq ($(CONFIG_USB_GADGET_MUSB_HDRC),y) + musb_hdrc-objs += musb_gadget_ep0.o musb_gadget.o + endif +--- a/drivers/usb/musb/musb_core.c ++++ b/drivers/usb/musb/musb_core.c +@@ -105,6 +105,13 @@ + #include <asm/mach-types.h> + #endif + ++#ifdef CONFIG_UBICOM32 ++#include <asm/ip5000.h> ++#include <asm/ubicom32-tio.h> ++extern void ubi32_usb_init(void); ++extern void ubi32_usb_int_clr(void); ++#endif ++ + #include "musb_core.h" + + +@@ -147,7 +154,7 @@ static inline struct musb *dev_to_musb(s + } + + /*-------------------------------------------------------------------------*/ +- ++#ifndef CONFIG_UBICOM32 + #ifndef CONFIG_USB_TUSB6010 + /* + * Load an endpoint's FIFO +@@ -226,8 +233,38 @@ void musb_read_fifo(struct musb_hw_ep *h + readsb(fifo, dst, len); + } + } ++#endif /* T6010 */ ++#else /* UBICOM */ ++ ++/* ++ * Load an endpoint's FIFO ++ */ ++void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 wCount, const u8 *pSource) ++{ ++ void __iomem *fifo = hw_ep->fifo; ++ ++ prefetch((u8 *)pSource); ++ ++ DBG(4, "%cX ep%d fifo %p count %d buf %p\n", ++ 'T', hw_ep->epnum, fifo, wCount, pSource); ++ ++ usb_tio_write_fifo((u32)fifo, (u32)pSource, wCount); ++ ++} + +-#endif /* normal PIO */ ++ ++/* ++ * Unload an endpoint's FIFO ++ */ ++void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 wCount, u8 *pDest) ++{ ++ ++ void __iomem *fifo = hw_ep->fifo; ++ DBG(4, "%cX ep%d fifo %p count %d buf %p\n", ++ 'R', hw_ep->epnum, fifo, wCount, pDest); ++ usb_tio_read_fifo((u32)fifo, (u32)pDest, wCount); ++} ++#endif /* CONFIG_UBICOM32 */ + + + /*-------------------------------------------------------------------------*/ +@@ -872,12 +909,19 @@ void musb_start(struct musb *musb) + musb_writeb(regs, MUSB_TESTMODE, 0); + + /* put into basic highspeed mode and start session */ ++#ifndef CONFIG_UBICOM32 + musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE + | MUSB_POWER_SOFTCONN + | MUSB_POWER_HSENAB + /* ENSUSPEND wedges tusb */ + /* | MUSB_POWER_ENSUSPEND */ + ); ++#else ++ musb_writeb(regs, MUSB_POWER, MUSB_POWER_HSENAB ++ /* ENSUSPEND wedges tusb */ ++ /* | MUSB_POWER_ENSUSPEND */ ++ ); ++#endif + + musb->is_active = 0; + devctl = musb_readb(regs, MUSB_DEVCTL); +@@ -1079,6 +1123,7 @@ static struct fifo_cfg __initdata mode_4 + }; + + ++#ifndef CONFIG_UBICOM32 + /* + * configure a fifo; for non-shared endpoints, this may be called + * once for a tx fifo and once for an rx fifo. +@@ -1238,7 +1283,7 @@ static int __init ep_config_from_table(s + + return 0; + } +- ++#endif /* CONFIG_UBICOM32 */ + + /* + * ep_config_from_hw - when MUSB_C_DYNFIFO_DEF is false +@@ -1253,13 +1298,23 @@ static int __init ep_config_from_hw(stru + DBG(2, "<== static silicon ep config\n"); + + /* FIXME pick up ep0 maxpacket size */ ++#ifdef CONFIG_UBICOM32 ++ /* set ep0 to shared_fifo, otherwise urb will be put to out_qh but ep0_irq try to get the urb from in_qh*/ ++ hw_ep = musb->endpoints; ++ hw_ep->is_shared_fifo = true; ++#endif + + for (epnum = 1; epnum < musb->config->num_eps; epnum++) { + musb_ep_select(mbase, epnum); + hw_ep = musb->endpoints + epnum; + + /* read from core using indexed model */ ++#ifndef CONFIG_UBICOM32 + reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE); ++#else ++ reg = musb_readb(musb->mregs, 0x10 + MUSB_FIFOSIZE); ++#endif ++ + if (!reg) { + /* 0's returned when no more endpoints */ + break; +@@ -1272,8 +1327,10 @@ static int __init ep_config_from_hw(stru + /* shared TX/RX FIFO? */ + if ((reg & 0xf0) == 0xf0) { + hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx; +- hw_ep->is_shared_fifo = true; +- continue; ++ hw_ep->is_shared_fifo = true; ++#ifndef CONFIG_UBICOM32 ++ continue; /* For ubicom processors, shared ep is all we get */ ++#endif + } else { + hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4); + hw_ep->is_shared_fifo = false; +@@ -1290,17 +1347,30 @@ static int __init ep_config_from_hw(stru + /* REVISIT: this algorithm is lazy, we should at least + * try to pick a double buffered endpoint. + */ ++#ifndef CONFIG_UBICOM32 + if (musb->bulk_ep) + continue; + musb->bulk_ep = hw_ep; ++#else ++ if ((musb->bulk_ep_in) && (musb->bulk_ep_out)) ++ continue; ++ /* Save theEP with 1024 Bytes FIFO for ISO */ ++ if(hw_ep->max_packet_sz_tx == 512) { ++ if (!musb->bulk_ep_in) { ++ musb->bulk_ep_in = hw_ep; ++ } else if (!musb->bulk_ep_out) { ++ musb->bulk_ep_out = hw_ep; ++ } ++ } ++#endif /* CONFIG_UBICOM32 */ + #endif + } + + #ifdef CONFIG_USB_MUSB_HDRC_HCD +- if (!musb->bulk_ep) { ++ if ((!musb->bulk_ep_in) || (!musb->bulk_ep_out)) { + pr_debug("%s: missing bulk\n", musb_driver_name); + return -EINVAL; +- } ++ } + #endif + + return 0; +@@ -1408,12 +1478,16 @@ static int __init musb_core_init(u16 mus + musb->epmask = 1; + + if (reg & MUSB_CONFIGDATA_DYNFIFO) { ++#ifndef CONFIG_UBICOM32 + if (musb->config->dyn_fifo) + status = ep_config_from_table(musb); +- else { ++ else ++#endif ++ { + ERR("reconfigure software for Dynamic FIFOs\n"); + status = -ENODEV; + } ++ + } else { + if (!musb->config->dyn_fifo) + status = ep_config_from_hw(musb); +@@ -1477,8 +1551,8 @@ static int __init musb_core_init(u16 mus + + /*-------------------------------------------------------------------------*/ + +-#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) +- ++#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) || defined(CONFIG_UBICOM32) ++static u32_t musb_int_count = 0; + static irqreturn_t generic_interrupt(int irq, void *__hci) + { + unsigned long flags; +@@ -1487,10 +1561,17 @@ static irqreturn_t generic_interrupt(int + + spin_lock_irqsave(&musb->lock, flags); + ++#ifndef CONFIG_UBICOM32 + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); ++#else ++ musb_read_int_status(&musb->int_usb, &musb->int_tx, &musb->int_rx); ++ //ubi32_usb_int_clr(); ++ musb_int_count++; ++#endif + ++ DBG(4, "usb %x, tx %x, rx %x", musb->int_usb, musb->int_tx, musb->int_rx); + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval = musb_interrupt(musb); + +@@ -2222,6 +2303,10 @@ static struct platform_driver musb_drive + + static int __init musb_init(void) + { ++#ifdef CONFIG_UBICOM32 ++ ubi32_usb_init(); ++#endif ++ + #ifdef CONFIG_USB_MUSB_HDRC_HCD + if (usb_disabled()) + return 0; +--- a/drivers/usb/musb/musb_core.h ++++ b/drivers/usb/musb/musb_core.h +@@ -326,7 +326,9 @@ struct musb { + * queue until it completes or NAKs too much; then we try the next + * endpoint. + */ +- struct musb_hw_ep *bulk_ep; ++ //struct musb_hw_ep *bulk_ep; ++ struct musb_hw_ep *bulk_ep_in; //ubicom ++ struct musb_hw_ep *bulk_ep_out; + + struct list_head control; /* of musb_qh */ + struct list_head in_bulk; /* of musb_qh */ +@@ -467,7 +469,7 @@ extern void musb_platform_disable(struct + + extern void musb_hnp_stop(struct musb *musb); + +-extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode); ++extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode); + + #if defined(CONFIG_USB_TUSB6010) || \ + defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +--- a/drivers/usb/musb/musb_gadget.c ++++ b/drivers/usb/musb/musb_gadget.c +@@ -421,7 +421,7 @@ void musb_g_tx(struct musb *musb, u8 epn + * probably rates reporting as a host error + */ + if (csr & MUSB_TXCSR_P_SENTSTALL) { +- csr |= MUSB_TXCSR_P_WZC_BITS; ++ csr &= ~(MUSB_TXCSR_P_WZC_BITS); + csr &= ~MUSB_TXCSR_P_SENTSTALL; + musb_writew(epio, MUSB_TXCSR, csr); + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { +@@ -437,7 +437,7 @@ void musb_g_tx(struct musb *musb, u8 epn + + if (csr & MUSB_TXCSR_P_UNDERRUN) { + /* we NAKed, no big deal ... little reason to care */ +- csr |= MUSB_TXCSR_P_WZC_BITS; ++ csr &= ~(MUSB_TXCSR_P_WZC_BITS); + csr &= ~(MUSB_TXCSR_P_UNDERRUN + | MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, csr); +@@ -573,10 +573,16 @@ static void rxstate(struct musb *musb, s + u16 csr = 0; + const u8 epnum = req->epnum; + struct usb_request *request = &req->request; +- struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; ++ struct musb_ep *musb_ep = NULL; + void __iomem *epio = musb->endpoints[epnum].regs; + u16 fifo_count = 0; +- u16 len = musb_ep->packet_sz; ++ u16 len = 0; ++ ++ if (musb->endpoints[epnum].is_shared_fifo) ++ musb_ep = &musb->endpoints[epnum].ep_in; ++ else ++ musb_ep = &musb->endpoints[epnum].ep_out; ++ len = musb_ep->packet_sz; + + csr = musb_readw(epio, MUSB_RXCSR); + +@@ -715,7 +721,7 @@ static void rxstate(struct musb *musb, s + */ + + /* ack the read! */ +- csr |= MUSB_RXCSR_P_WZC_BITS; ++ csr &= ~MUSB_RXCSR_P_WZC_BITS; + csr &= ~MUSB_RXCSR_RXPKTRDY; + musb_writew(epio, MUSB_RXCSR, csr); + } +@@ -734,10 +740,15 @@ void musb_g_rx(struct musb *musb, u8 epn + u16 csr; + struct usb_request *request; + void __iomem *mbase = musb->mregs; +- struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; ++ struct musb_ep *musb_ep = NULL; + void __iomem *epio = musb->endpoints[epnum].regs; + struct dma_channel *dma; + ++ if (musb->endpoints[epnum].is_shared_fifo) ++ musb_ep = &musb->endpoints[epnum].ep_in; ++ else ++ musb_ep = &musb->endpoints[epnum].ep_out; ++ + musb_ep_select(mbase, epnum); + + request = next_request(musb_ep); +@@ -1559,7 +1570,7 @@ init_peripheral_ep(struct musb *musb, st + ep->is_in = is_in; + + INIT_LIST_HEAD(&ep->req_list); +- ++ + sprintf(ep->name, "ep%d%s", epnum, + (!epnum || hw_ep->is_shared_fifo) ? "" : ( + is_in ? "in" : "out")); +@@ -1758,7 +1769,9 @@ int usb_gadget_register_driver(struct us + } + } + } +- ++#ifndef CONFIG_USB_MUSB_OTG ++ musb_pullup(musb, 1); ++#endif + return retval; + } + EXPORT_SYMBOL(usb_gadget_register_driver); +--- a/drivers/usb/musb/musb_gadget_ep0.c ++++ b/drivers/usb/musb/musb_gadget_ep0.c +@@ -240,14 +240,14 @@ __acquires(musb->lock) + case USB_REQ_SET_ADDRESS: + /* change it after the status stage */ + musb->set_address = true; +- musb->address = (u8) (ctrlrequest->wValue & 0x7f); ++ musb->address = (u8) (le16_to_cpu(ctrlrequest->wValue) & 0x7f); + handled = 1; + break; + + case USB_REQ_CLEAR_FEATURE: + switch (recip) { + case USB_RECIP_DEVICE: +- if (ctrlrequest->wValue ++ if (le16_to_cpu(ctrlrequest->wValue) + != USB_DEVICE_REMOTE_WAKEUP) + break; + musb->may_wakeup = 0; +@@ -261,8 +261,8 @@ __acquires(musb->lock) + + if (num == 0 + || num >= MUSB_C_NUM_EPS +- || ctrlrequest->wValue +- != USB_ENDPOINT_HALT) ++ || le16_to_cpu(ctrlrequest->wValue ++ != USB_ENDPOINT_HALT)) + break; + + if (ctrlrequest->wIndex & USB_DIR_IN) +@@ -292,7 +292,7 @@ __acquires(musb->lock) + switch (recip) { + case USB_RECIP_DEVICE: + handled = 1; +- switch (ctrlrequest->wValue) { ++ switch (le16_to_cpu(ctrlrequest->wValue)) { + case USB_DEVICE_REMOTE_WAKEUP: + musb->may_wakeup = 1; + break; +@@ -374,8 +374,8 @@ stall: + + if (epnum == 0 + || epnum >= MUSB_C_NUM_EPS +- || ctrlrequest->wValue +- != USB_ENDPOINT_HALT) ++ || le16_to_cpu(ctrlrequest->wValue ++ != USB_ENDPOINT_HALT)) + break; + + ep = musb->endpoints + epnum; +--- a/drivers/usb/musb/musb_host.c ++++ b/drivers/usb/musb/musb_host.c +@@ -139,7 +139,11 @@ static inline void musb_h_tx_start(struc + /* NOTE: no locks here; caller should lock and select EP */ + if (ep->epnum) { + txcsr = musb_readw(ep->regs, MUSB_TXCSR); +- txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_H_WZC_BITS; ++#ifndef CONFIG_UBICOM32 ++ txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_H_WZC_BITS; ++#else ++ txcsr |= (MUSB_TXCSR_TXPKTRDY & (~MUSB_TXCSR_H_WZC_BITS)); ++#endif + musb_writew(ep->regs, MUSB_TXCSR, txcsr); + } else { + txcsr = MUSB_CSR0_H_SETUPPKT | MUSB_CSR0_TXPKTRDY; +@@ -198,8 +202,11 @@ musb_start_urb(struct musb *musb, int is + len = urb->iso_frame_desc[0].length; + break; + default: /* bulk, interrupt */ +- buf = urb->transfer_buffer; +- len = urb->transfer_buffer_length; ++ /* actual_length may be nonzero on retry paths */ ++ if (urb->actual_length) ++ DBG(3 ,"musb_start_urb: URB %p retried, len: %d\n", urb, urb->actual_length); ++ buf = urb->transfer_buffer + urb->actual_length; ++ len = urb->transfer_buffer_length - urb->actual_length; + } + + DBG(4, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n", +@@ -318,13 +325,13 @@ musb_save_toggle(struct musb_hw_ep *ep, + if (!is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + usb_settoggle(udev, qh->epnum, 1, +- (csr & MUSB_TXCSR_H_DATATOGGLE) +- ? 1 : 0); ++ ((csr & MUSB_TXCSR_H_DATATOGGLE) ++ ? 1 : 0)); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + usb_settoggle(udev, qh->epnum, 0, +- (csr & MUSB_RXCSR_H_DATATOGGLE) +- ? 1 : 0); ++ ((csr & MUSB_RXCSR_H_DATATOGGLE) ++ ? 1 : 0)); + } + } + +@@ -337,9 +344,11 @@ musb_giveback(struct musb_qh *qh, struct + struct musb *musb = ep->musb; + int ready = qh->is_ready; + ++#ifndef CONFIG_UBICOM32 /* BUG! */ + if (ep->is_shared_fifo) + is_in = 1; + else ++#endif + is_in = usb_pipein(urb->pipe); + + /* save toggle eagerly, for paranoia */ +@@ -538,7 +547,11 @@ musb_host_packet_rx(struct musb *musb, s + musb_read_fifo(hw_ep, length, buf); + + csr = musb_readw(epio, MUSB_RXCSR); ++#ifndef CONFIG_UBICOM32 + csr |= MUSB_RXCSR_H_WZC_BITS; ++#else ++ csr &= ~MUSB_RXCSR_H_WZC_BITS; ++#endif + if (unlikely(do_flush)) + musb_h_flush_rxfifo(hw_ep, csr); + else { +@@ -572,14 +585,24 @@ musb_rx_reinit(struct musb *musb, struct + + /* if programmed for Tx, put it in RX mode */ + if (ep->is_shared_fifo) { ++#ifndef CONFIG_UBICOM32 + csr = musb_readw(ep->regs, MUSB_TXCSR); + if (csr & MUSB_TXCSR_MODE) { + musb_h_tx_flush_fifo(ep); + musb_writew(ep->regs, MUSB_TXCSR, + MUSB_TXCSR_FRCDATATOG); + } ++#else + /* clear mode (and everything else) to enable Rx */ + musb_writew(ep->regs, MUSB_TXCSR, 0); ++ /* scrub all previous state, clearing toggle */ ++ csr = musb_readw(ep->regs, MUSB_RXCSR); ++ if (csr & MUSB_RXCSR_RXPKTRDY) ++ WARNING("rx%d, packet/%d ready?\n", ep->epnum, ++ musb_readw(ep->regs, MUSB_RXCOUNT)); ++ ++ musb_h_flush_rxfifo(ep, MUSB_RXCSR_CLRDATATOG); ++#endif + + /* scrub all previous state, clearing toggle */ + } else { +@@ -680,7 +703,7 @@ static void musb_ep_program(struct musb + /* ASSERT: TXCSR_DMAENAB was already cleared */ + + /* flush all old state, set default */ +- musb_h_tx_flush_fifo(hw_ep); ++ musb_h_tx_flush_fifo(hw_ep); + csr &= ~(MUSB_TXCSR_H_NAKTIMEOUT + | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_FRCDATATOG +@@ -1169,8 +1192,18 @@ void musb_host_tx(struct musb *musb, u8 + void __iomem *mbase = musb->mregs; + struct dma_channel *dma; + ++#ifdef CONFIG_UBICOM32 ++ if (hw_ep->is_shared_fifo) { ++ qh = hw_ep->in_qh; ++ } ++#ifdef CONFIG_USB_SERIAL_SIERRAWIRELESS ++ printk(KERN_DEBUG "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, ++ dma ? ", dma" : ""); ++#endif ++#endif + urb = next_urb(qh); + ++ + musb_ep_select(mbase, epnum); + tx_csr = musb_readw(epio, MUSB_TXCSR); + +@@ -1210,9 +1243,14 @@ void musb_host_tx(struct musb *musb, u8 + * we have a candidate... NAKing is *NOT* an error + */ + musb_ep_select(mbase, epnum); ++#ifndef CONFIG_UBICOM32 + musb_writew(epio, MUSB_TXCSR, + MUSB_TXCSR_H_WZC_BITS + | MUSB_TXCSR_TXPKTRDY); ++#else ++ musb_writew(epio, MUSB_TXCSR, ++ MUSB_TXCSR_TXPKTRDY); ++#endif + goto finish; + } + +@@ -1316,8 +1354,14 @@ void musb_host_tx(struct musb *musb, u8 + qh->segsize = wLength; + + musb_ep_select(mbase, epnum); ++#ifndef CONFIG_UBICOM32 + musb_writew(epio, MUSB_TXCSR, +- MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); ++ MUSB_TXCSR_MODE | MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); ++#else ++ musb_writew(epio, MUSB_TXCSR, ++ MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY); ++#endif ++ + } else + DBG(1, "not complete, but dma enabled?\n"); + +@@ -1365,6 +1409,49 @@ finish: + + #endif + ++/* Schedule next QH from musb->in_bulk and move the current qh to ++ * the end; avoids starvation for other endpoints. ++ */ ++static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep) ++{ ++ struct dma_channel *dma; ++ struct urb *urb; ++ void __iomem *mbase = musb->mregs; ++ void __iomem *epio = ep->regs; ++ struct musb_qh *cur_qh, *next_qh; ++ u16 rx_csr; ++ ++ musb_ep_select(mbase, ep->epnum); ++ dma = is_dma_capable() ? ep->rx_channel : NULL; ++ /* clear nak timeout bit */ ++ rx_csr = musb_readw(epio, MUSB_RXCSR); ++ rx_csr &= ~MUSB_RXCSR_H_WZC_BITS; ++ rx_csr &= ~MUSB_RXCSR_DATAERROR; ++ musb_writew(epio, MUSB_RXCSR, rx_csr); ++ ++ cur_qh = first_qh(&musb->in_bulk); ++ if (cur_qh) { ++ urb = next_urb(cur_qh); ++ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { ++ dma->status = MUSB_DMA_STATUS_CORE_ABORT; ++ musb->dma_controller->channel_abort(dma); ++ urb->actual_length += dma->actual_len; ++ dma->actual_len = 0L; ++ } ++ musb_save_toggle(ep, 1, urb); ++ ++ /* move cur_qh to end of queue */ ++ list_move_tail(&cur_qh->ring, &musb->in_bulk); ++ ++ /* get the next qh from musb->in_bulk */ ++ next_qh = first_qh(&musb->in_bulk); ++ ++ /* set rx_reinit and schedule the next qh */ ++ ep->rx_reinit = 1; ++ musb_start_urb(musb, 1, next_qh); ++ } ++} ++ + /* + * Service an RX interrupt for the given IN endpoint; docs cover bulk, iso, + * and high-bandwidth IN transfer cases. +@@ -1383,7 +1470,7 @@ void musb_host_rx(struct musb *musb, u8 + bool done = false; + u32 status; + struct dma_channel *dma; +- ++ + musb_ep_select(mbase, epnum); + + urb = next_urb(qh); +@@ -1407,6 +1494,13 @@ void musb_host_rx(struct musb *musb, u8 + + pipe = urb->pipe; + ++#ifdef CONFIG_UBICOM32 ++#ifdef CONFIG_USB_SERIAL_SIERRAWIRELESS ++ printk(KERN_DEBUG "RXCSR%d %04x, reqpkt, len %zu%s\n", epnum, rx_csr, ++ xfer_len, dma ? ", dma" : ""); ++#endif ++#endif ++ + DBG(5, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", + epnum, rx_csr, urb->actual_length, + dma ? dma->actual_len : 0); +@@ -1428,18 +1522,30 @@ void musb_host_rx(struct musb *musb, u8 + } else if (rx_csr & MUSB_RXCSR_DATAERROR) { + + if (USB_ENDPOINT_XFER_ISOC != qh->type) { +- /* NOTE this code path would be a good place to PAUSE a +- * transfer, if there's some other (nonperiodic) rx urb +- * that could use this fifo. (dma complicates it...) ++ DBG(6, "RX end %d NAK timeout\n", epnum); ++ ++ /* NOTE: NAKing is *NOT* an error, so we want to ++ * continue. Except ... if there's a request for ++ * another QH, use that instead of starving it. + * +- * if (bulk && qh->ring.next != &musb->in_bulk), then +- * we have a candidate... NAKing is *NOT* an error ++ * Devices like Ethernet and serial adapters keep ++ * reads posted at all times, which will starve ++ * other devices without this logic. + */ +- DBG(6, "RX end %d NAK timeout\n", epnum); ++ if (usb_pipebulk(urb->pipe) ++ && qh->mux == 1 ++ && !list_is_singular(&musb->in_bulk)) { ++ musb_bulk_rx_nak_timeout(musb, hw_ep); ++ return; ++ } + musb_ep_select(mbase, epnum); ++#ifndef CONFIG_UBICOM32 + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS + | MUSB_RXCSR_H_REQPKT); ++#else ++ musb_writew(epio, MUSB_RXCSR, (~(MUSB_RXCSR_H_WZC_BITS))| MUSB_RXCSR_H_REQPKT); ++#endif + + goto finish; + } else { +@@ -1495,8 +1601,13 @@ void musb_host_rx(struct musb *musb, u8 + rx_csr &= ~MUSB_RXCSR_H_REQPKT; + + musb_ep_select(mbase, epnum); ++#ifndef CONFIG_UBICOM32 + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS | rx_csr); ++#else ++ musb_writew(epio, MUSB_RXCSR, ++ (~MUSB_RXCSR_H_WZC_BITS) & rx_csr); ++#endif + } + #endif + if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) { +@@ -1526,7 +1637,7 @@ void musb_host_rx(struct musb *musb, u8 + else + done = false; + +- } else { ++ } else { + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + xfer_len >= + urb->transfer_buffer_length +@@ -1743,8 +1854,12 @@ static int musb_schedule( + if (musb->periodic[epnum]) + continue; + hw_ep = &musb->endpoints[epnum]; ++#ifndef CONFIG_UBICOM32 + if (hw_ep == musb->bulk_ep) +- continue; ++#else ++ if ((hw_ep == musb->bulk_ep_in) || (hw_ep == musb->bulk_ep_out)) /* Ubicom */ ++#endif ++ continue; + + if (is_in) + diff = hw_ep->max_packet_sz_rx - qh->maxpacket; +@@ -1756,7 +1871,14 @@ static int musb_schedule( + best_end = epnum; + } + } ++ ++#ifdef CONFIG_UBICOM32 ++ if (((best_diff >= qh->maxpacket)) && ((qh->type == USB_ENDPOINT_XFER_BULK) && (!is_in))) ++ best_end = -1; ++#endif ++ + /* use bulk reserved ep1 if no other ep is free */ ++#ifndef CONFIG_UBICOM32 + if (best_end < 0 && qh->type == USB_ENDPOINT_XFER_BULK) { + hw_ep = musb->bulk_ep; + if (is_in) +@@ -1767,6 +1889,22 @@ static int musb_schedule( + } else if (best_end < 0) { + return -ENOSPC; + } ++#else ++ if (best_end < 0 && qh->type == USB_ENDPOINT_XFER_BULK) { ++ /* hw_ep = musb->bulk_ep; */ ++ if (is_in) { ++ head = &musb->in_bulk; ++ hw_ep = musb->bulk_ep_in; /* UBICOM */ ++ } ++ else { ++ head = &musb->out_bulk; ++ hw_ep = musb->bulk_ep_out; /* UBICOM */ ++ } ++ goto success; ++ } else if (best_end < 0) { ++ return -ENOSPC; ++ } ++#endif + + idle = 1; + qh->mux = 0; +@@ -1779,6 +1917,13 @@ success: + list_add_tail(&qh->ring, head); + qh->mux = 1; + } ++ /* ++ * It's not make sense to set NAK timeout when qh->mux = 0, ++ * There is nothing else to schedule ++ */ ++ if ((qh->type == USB_ENDPOINT_XFER_BULK) && (qh->mux == 0)) ++ qh->intv_reg = 0; ++ + qh->hw_ep = hw_ep; + qh->hep->hcpriv = qh; + if (idle) +@@ -1884,6 +2029,15 @@ static int musb_urb_enqueue( + case USB_ENDPOINT_XFER_ISOC: + /* iso always uses log encoding */ + break; ++#ifdef COMFIG_UBICOM32 ++ case USB_ENDPOINT_XFER_BULK: ++ if (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ++ interval = (USB_SPEED_HIGH == urb->dev->speed) ? 16: 2; ++ else ++ interval = 0; ++ break; ++#endif ++ + default: + /* REVISIT we actually want to use NAK limits, hinting to the + * transfer scheduling logic to try some other qh, e.g. try +--- a/drivers/usb/musb/musb_io.h ++++ b/drivers/usb/musb/musb_io.h +@@ -56,6 +56,7 @@ static inline void writesb(const void __ + + #endif + ++#ifndef CONFIG_UBICOM32 + /* NOTE: these offsets are all in bytes */ + + static inline u16 musb_readw(const void __iomem *addr, unsigned offset) +@@ -70,7 +71,37 @@ static inline void musb_writew(void __io + + static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data) + { __raw_writel(data, addr + offset); } ++#else ++#include <asm/ubicom32-tio.h> ++static inline u16 musb_readw(const void __iomem *addr, unsigned offset) ++{ ++ u16 data; ++ usb_tio_read_u16((u32)(addr + offset), &data); ++ return data; ++} + ++static inline u8 musb_readb(const void __iomem *addr, unsigned offset) ++{ ++ u8 data; ++ usb_tio_read_u8((u32)(addr + offset), &data); ++ return data; ++} ++ ++static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data) ++{ ++ usb_tio_write_u16((u32)(addr + offset), data); ++} ++ ++static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) ++{ ++ usb_tio_write_u8((u32)(addr + offset), data); ++} ++ ++static inline void musb_read_int_status(u8_t *int_usb, u16_t *int_tx, u16_t *int_rx) ++{ ++ return usb_tio_read_int_status(int_usb, int_tx, int_rx); ++} ++#endif /* CONFIG_UBICOM32 */ + + #ifdef CONFIG_USB_TUSB6010 + +@@ -104,7 +135,7 @@ static inline void musb_writeb(void __io + __raw_writew(tmp, addr + (offset & ~1)); + } + +-#else ++#elif !defined(CONFIG_UBICOM32) + + static inline u8 musb_readb(const void __iomem *addr, unsigned offset) + { return __raw_readb(addr + offset); } +--- /dev/null ++++ b/drivers/usb/musb/ubi32_usb.c +@@ -0,0 +1,156 @@ ++/* ++ * drivers/usb/musb/ubi32_usb.c ++ * Ubicom32 usb controller driver. ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * Copyright (C) 2005-2006 by Texas Instruments ++ * ++ * Derived from the Texas Instruments Inventra Controller Driver for Linux. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/clk.h> ++#include <linux/io.h> ++ ++#include <asm/io.h> ++#include <asm/ip5000.h> ++#include "musb_core.h" ++ ++void musb_platform_enable(struct musb *musb) ++{ ++} ++void musb_platform_disable(struct musb *musb) ++{ ++} ++ ++int musb_platform_set_mode(struct musb *musb, u8 musb_mode) { ++ return 0; ++} ++ ++static void ip5k_usb_hcd_vbus_power(struct musb *musb, int is_on, int sleeping) ++{ ++} ++ ++static void ip5k_usb_hcd_set_vbus(struct musb *musb, int is_on) ++{ ++ u8 devctl; ++ /* HDRC controls CPEN, but beware current surges during device ++ * connect. They can trigger transient overcurrent conditions ++ * that must be ignored. ++ */ ++ ++ devctl = musb_readb(musb->mregs, MUSB_DEVCTL); ++ ++ if (is_on) { ++ musb->is_active = 1; ++ musb->xceiv.default_a = 1; ++ musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; ++ devctl |= MUSB_DEVCTL_SESSION; ++ ++ MUSB_HST_MODE(musb); ++ } else { ++ musb->is_active = 0; ++ ++ /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and ++ * jumping right to B_IDLE... ++ */ ++ ++ musb->xceiv.default_a = 0; ++ musb->xceiv.state = OTG_STATE_B_IDLE; ++ devctl &= ~MUSB_DEVCTL_SESSION; ++ ++ MUSB_DEV_MODE(musb); ++ } ++ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); ++ ++ DBG(1, "VBUS %s, devctl %02x " ++ /* otg %3x conf %08x prcm %08x */ "\n", ++ otg_state_string(musb), ++ musb_readb(musb->mregs, MUSB_DEVCTL)); ++} ++static int ip5k_usb_hcd_set_power(struct otg_transceiver *x, unsigned mA) ++{ ++ return 0; ++} ++ ++static int musb_platform_resume(struct musb *musb); ++ ++int __init musb_platform_init(struct musb *musb) ++{ ++ ++#ifdef CONFIG_UBICOM32_V4 ++ u32_t chip_id; ++ asm volatile ( ++ "move.4 %0, CHIP_ID \n\t" ++ : "=r" (chip_id) ++ ); ++ if (chip_id == 0x30001) { ++ *((u32_t *)(GENERAL_CFG_BASE + GEN_USB_PHY_TEST)) &= ~(1 << 30); ++ udelay(1); ++ *((u32_t *)(GENERAL_CFG_BASE + GEN_USB_PHY_TEST)) &= ~(1 << 31); ++ } else { ++ *((u32_t *)(GENERAL_CFG_BASE + GEN_USB_PHY_TEST)) &= ~(1 << 17); ++ udelay(1); ++ *((u32_t *)(GENERAL_CFG_BASE + GEN_USB_PHY_TEST)) &= ~(1 << 14); ++ } ++#endif ++ ++ *((u32_t *)(GENERAL_CFG_BASE + GEN_USB_PHY_CFG)) |= ((1 << 14) | (1 <<15)); ++ ++ /* The i-clk is AUTO gated. Hence there is no need ++ * to disable it until the driver is shutdown */ ++ ++ clk_enable(musb->clock); ++ musb_platform_resume(musb); ++ ++ ip5k_usb_hcd_vbus_power(musb, musb->board_mode == MUSB_HOST, 1); ++ ++ if (is_host_enabled(musb)) ++ musb->board_set_vbus = ip5k_usb_hcd_set_vbus; ++ if (is_peripheral_enabled(musb)) ++ musb->xceiv.set_power = ip5k_usb_hcd_set_power; ++ ++ return 0; ++} ++ ++ ++int musb_platform_suspend(struct musb *musb) ++{ ++ return 0; ++} ++int musb_platform_resume(struct musb *musb) ++{ ++ return 0; ++} ++ ++int musb_platform_exit(struct musb *musb) ++{ ++ ip5k_usb_hcd_vbus_power(musb, 0 /*off*/, 1); ++ musb_platform_suspend(musb); ++ return 0; ++} +--- a/drivers/video/backlight/Kconfig ++++ b/drivers/video/backlight/Kconfig +@@ -84,6 +84,14 @@ config LCD_TOSA + If you have an Sharp SL-6000 Zaurus say Y to enable a driver + for its LCD. + ++config LCD_UBICOM32 ++ tristate "Ubicom Backlight Driver" ++ depends on LCD_CLASS_DEVICE && UBICOM32 ++ default n ++ help ++ If you have a Ubicom32 based system with an LCD panel, say Y to enable ++ the power control driver for it. ++ + # + # Backlight + # +@@ -214,3 +222,11 @@ config BACKLIGHT_SAHARA + help + If you have a Tabletkiosk Sahara Touch-iT, say y to enable the + backlight driver. ++ ++config BACKLIGHT_UBICOM32 ++ tristate "Ubicom Backlight Driver" ++ depends on BACKLIGHT_CLASS_DEVICE && UBICOM32 ++ default n ++ help ++ If you have a Ubicom32 based system with a backlight say Y to enable the ++ backlight driver. +--- a/drivers/video/backlight/Makefile ++++ b/drivers/video/backlight/Makefile +@@ -8,6 +8,8 @@ obj-$(CONFIG_LCD_PLATFORM) += platfor + obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o + obj-$(CONFIG_LCD_TDO24M) += tdo24m.o + obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o ++obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o ++obj-$(CONFIG_LCD_UBICOM32) += ubicom32lcd.o + + obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o + obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o +@@ -22,4 +24,4 @@ obj-$(CONFIG_BACKLIGHT_DA903X) += da903x + obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o + obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o + obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o +- ++obj-$(CONFIG_BACKLIGHT_UBICOM32) += ubicom32bl.o +--- /dev/null ++++ b/drivers/video/backlight/ubicom32bl.c +@@ -0,0 +1,378 @@ ++/* ++ * drivers/video/backlight/ubicom32bl.c ++ * Backlight driver for the Ubicom32 platform ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/backlight.h> ++#include <linux/fb.h> ++ ++#include <asm/ubicom32bl.h> ++#include <asm/ip5000.h> ++ ++#define DRIVER_NAME "ubicom32bl" ++#define UBICOM32BL_MAX_BRIGHTNESS 255 ++ ++struct ubicom32bl_data { ++ /* ++ * Pointer to the platform data structure. Keep this around since we need values ++ * from it to set the backlight intensity. ++ */ ++ const struct ubicom32bl_platform_data *pdata; ++ ++ /* ++ * Backlight device, we have to save this for use when we remove ourselves. ++ */ ++ struct backlight_device *bldev; ++ ++ /* ++ * Current intensity, used for get_intensity. ++ */ ++ int cur_intensity; ++ ++ /* ++ * Init function for PWM ++ */ ++ int (*init_fn)(struct ubicom32bl_data *); ++ ++ /* ++ * Set intensity function depending on the backlight type ++ */ ++ int (*set_intensity_fn)(struct ubicom32bl_data *, int); ++}; ++ ++/* ++ * ubicom32bl_set_intensity_gpio ++ */ ++static int ubicom32bl_set_intensity_gpio(struct ubicom32bl_data *ud, int intensity) ++{ ++ ud->cur_intensity = intensity ? 255 : 0; ++ ++ if (intensity) { ++ // set gpio ++ return 0; ++ } ++ ++ // clear gpio ++ return 0; ++} ++ ++/* ++ * ubicom32bl_set_intensity_hw ++ */ ++static int ubicom32bl_set_intensity_hw(struct ubicom32bl_data *ud, int intensity) ++{ ++ u16_t period = ud->pdata->pwm_period; ++ u16_t duty; ++ ++ /* ++ * Calculate the new duty cycle ++ */ ++ duty = (period * intensity) / (UBICOM32BL_MAX_BRIGHTNESS + 1); ++ ++ /* ++ * Set the new duty cycle ++ */ ++ switch (ud->pdata->pwm_channel) { ++ case 0: ++ /* ++ * Channel 0 is in the lower half of PORT C ctl0 and ctl1 ++ */ ++ UBICOM32_IO_PORT(RC)->ctl1 = (ud->pdata->pwm_period << 16) | duty; ++ break; ++ ++ case 1: ++ /* ++ * Channel 1 is in the upper half of PORT C ctl0 and ctl2 ++ */ ++ UBICOM32_IO_PORT(RC)->ctl2 = (ud->pdata->pwm_period << 16) | duty; ++ break; ++ ++ case 2: ++ /* ++ * Channel 2 is in PORT H ctl0 and ctl1 ++ */ ++ UBICOM32_IO_PORT(RH)->ctl1 = (ud->pdata->pwm_period << 16) | duty; ++ break; ++ } ++ ++ ud->cur_intensity = intensity; ++ ++ return 0; ++} ++ ++/* ++ * ubicom32bl_set_intensity ++ */ ++static int ubicom32bl_set_intensity(struct backlight_device *bd) ++{ ++ struct ubicom32bl_data *ud = (struct ubicom32bl_data *)bl_get_data(bd); ++ int intensity = bd->props.brightness; ++ ++ /* ++ * If we're blanked the the intensity doesn't matter. ++ */ ++ if ((bd->props.power != FB_BLANK_UNBLANK) || (bd->props.fb_blank != FB_BLANK_UNBLANK)) { ++ intensity = 0; ++ } ++ ++ /* ++ * Check for inverted backlight. ++ */ ++ if (ud->pdata->invert) { ++ intensity = UBICOM32BL_MAX_BRIGHTNESS - intensity; ++ } ++ ++ if (ud->set_intensity_fn) { ++ return ud->set_intensity_fn(ud, intensity); ++ } ++ ++ return -ENXIO; ++} ++ ++/* ++ * ubicom32bl_get_intensity ++ * Return the current intensity of the backlight. ++ */ ++static int ubicom32bl_get_intensity(struct backlight_device *bd) ++{ ++ struct ubicom32bl_data *ud = (struct ubicom32bl_data *)bl_get_data(bd); ++ ++ return ud->cur_intensity; ++} ++ ++/* ++ * ubicom32bl_init_hw_pwm ++ * Set the appropriate PWM registers ++ */ ++static int ubicom32bl_init_hw_pwm(struct ubicom32bl_data *ud) ++{ ++ /* ++ * bit 13: enable ++ */ ++ u16_t pwm_cfg = (1 << 13) | (ud->pdata->pwm_prescale << 8) ; ++ ++ switch (ud->pdata->pwm_channel) { ++ case 0: ++ /* ++ * Channel 0 is in the lower half of PORT C ctl0 and ctl1 ++ */ ++ UBICOM32_IO_PORT(RC)->ctl0 &= ~0xFFFF; ++ UBICOM32_IO_PORT(RC)->ctl0 |= pwm_cfg; ++ UBICOM32_IO_PORT(RC)->ctl1 = ud->pdata->pwm_period << 16; ++ break; ++ ++ case 1: ++ /* ++ * Channel 1 is in the upper half of PORT C ctl0 and ctl2 ++ */ ++ UBICOM32_IO_PORT(RC)->ctl0 &= ~0xFFFF0000; ++ UBICOM32_IO_PORT(RC)->ctl0 |= (pwm_cfg << 16); ++ UBICOM32_IO_PORT(RC)->ctl2 = ud->pdata->pwm_period << 16; ++ break; ++ ++ case 2: ++ /* ++ * Channel 2 is in PORT H ctl0 and ctl1 ++ */ ++ UBICOM32_IO_PORT(RH)->ctl0 &= ~0xFFFF0000; ++ UBICOM32_IO_PORT(RH)->ctl0 = pwm_cfg; ++ UBICOM32_IO_PORT(RH)->ctl1 = ud->pdata->pwm_period << 16; ++ break; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ubicom32bl_init_gpio ++ * Allocate the appropriate GPIO ++ */ ++static int ubicom32bl_init_gpio(struct ubicom32bl_data *ud) ++{ ++ return 0; ++} ++ ++static struct backlight_ops ubicom32bl_ops = { ++ .get_brightness = ubicom32bl_get_intensity, ++ .update_status = ubicom32bl_set_intensity, ++}; ++ ++/* ++ * ubicom32bl_probe ++ */ ++static int ubicom32bl_probe(struct platform_device *pdev) ++{ ++ const struct ubicom32bl_platform_data *pdata = pdev->dev.platform_data; ++ struct ubicom32bl_data *ud; ++ struct backlight_device *bldev; ++ int retval; ++ ++ /* ++ * Check to see if we have any platform data, if we don't then the backlight is not ++ * configured on this device. ++ */ ++ if (!pdata) { ++ return -ENODEV; ++ } ++ ++ /* ++ * Allocate our private data ++ */ ++ ud = kzalloc(sizeof(struct ubicom32bl_data), GFP_KERNEL); ++ if (!ud) { ++ return -ENOMEM; ++ } ++ ++ ud->pdata = pdata; ++ ++ /* ++ * Check to see that the platform data is valid for this driver ++ */ ++ switch (pdata->type) { ++ case UBICOM32BL_TYPE_PWM: ++ { ++ /* ++ * Make sure we have a PWM peripheral ++ */ ++ u32_t chipid; ++ asm volatile ( ++ "move.4 %0, CHIP_ID \n\t" ++ : "=r" (chipid) ++ ); ++ if (chipid != 0x00030001) { ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ if (pdata->pwm_channel > 3) { ++ retval = -ENODEV; ++ goto fail; ++ } ++ if (pdata->pwm_prescale > 16) { ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ ud->init_fn = ubicom32bl_init_hw_pwm; ++ ud->set_intensity_fn = ubicom32bl_set_intensity_hw; ++ break; ++ } ++ ++ case UBICOM32BL_TYPE_PWM_HRT: ++ // For now, PWM HRT devices are treated as binary lights. ++ ++ case UBICOM32BL_TYPE_BINARY: ++ ud->init_fn = ubicom32bl_init_gpio; ++ ud->set_intensity_fn = ubicom32bl_set_intensity_gpio; ++ break; ++ } ++ ++ /* ++ * Register our backlight device ++ */ ++ bldev = backlight_device_register(DRIVER_NAME, &pdev->dev, ud, &ubicom32bl_ops); ++ if (IS_ERR(bldev)) { ++ retval = PTR_ERR(bldev); ++ goto fail; ++ } ++ ++ ud->bldev = bldev; ++ ud->cur_intensity = pdata->default_intensity; ++ platform_set_drvdata(pdev, ud); ++ ++ /* ++ * Start up the backlight at the prescribed default intensity ++ */ ++ bldev->props.power = FB_BLANK_UNBLANK; ++ bldev->props.max_brightness = UBICOM32BL_MAX_BRIGHTNESS; ++ bldev->props.brightness = pdata->default_intensity; ++ ++ if (ud->init_fn) { ++ if (ud->init_fn(ud) != 0) { ++ retval = -ENODEV; ++ backlight_device_unregister(ud->bldev); ++ goto fail; ++ } ++ } ++ ubicom32bl_set_intensity(bldev); ++ ++ printk(KERN_INFO DRIVER_NAME ": Backlight driver started\n"); ++ ++ return 0; ++ ++fail: ++ platform_set_drvdata(pdev, NULL); ++ kfree(ud); ++ return retval; ++} ++ ++/* ++ * ubicom32bl_remove ++ */ ++static int __exit ubicom32bl_remove(struct platform_device *pdev) ++{ ++ struct ubicom32bl_data *ud = platform_get_drvdata(pdev); ++ ++ backlight_device_unregister(ud->bldev); ++ platform_set_drvdata(pdev, NULL); ++ kfree(ud); ++ ++ return 0; ++} ++ ++static struct platform_driver ubicom32bl_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ ++ .remove = __exit_p(ubicom32bl_remove), ++}; ++ ++/* ++ * ubicom32bl_init ++ */ ++static int __init ubicom32bl_init(void) ++{ ++ return platform_driver_probe(&ubicom32bl_driver, ubicom32bl_probe); ++} ++module_init(ubicom32bl_init); ++ ++/* ++ * ubicom32bl_exit ++ */ ++static void __exit ubicom32bl_exit(void) ++{ ++ platform_driver_unregister(&ubicom32bl_driver); ++} ++module_exit(ubicom32bl_exit); ++ ++MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); ++MODULE_DESCRIPTION("Ubicom32 backlight driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/video/backlight/ubicom32lcd.c +@@ -0,0 +1,194 @@ ++/* ++ * drivers/vdeio/backlight/ubicom32lcd.c ++ * LCD driver for the Ubicom32 platform ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/lcd.h> ++#include <linux/fb.h> ++#include <linux/gpio.h> ++ ++#include <asm/ubicom32lcd.h> ++#include <asm/ip5000.h> ++ ++#define DRIVER_NAME "ubicom32lcd" ++ ++struct ubicom32lcd_data { ++ /* ++ * Pointer to the platform data structure. Keep this around since we need values ++ * from it to set the backlight intensity. ++ */ ++ const struct ubicom32lcd_platform_data *pdata; ++ ++ /* ++ * LCD device, we have to save this for use when we remove ourselves. ++ */ ++ struct lcd_device *lcddev; ++}; ++ ++/* ++ * ubicom32lcd_set_power ++ */ ++static int ubicom32lcd_set_power(struct lcd_device *ld, int power) ++{ ++ struct ubicom32lcd_data *ud = (struct ubicom32lcd_data *)lcd_get_data(ld); ++ if (power == FB_BLANK_UNBLANK) { ++ gpio_direction_output(ud->pdata->vgh_gpio, ud->pdata->vgh_polarity); ++ return 0; ++ } ++ ++ gpio_direction_output(ud->pdata->vgh_gpio, !ud->pdata->vgh_polarity); ++ return 0; ++} ++ ++/* ++ * ubicom32lcd_get_power ++ */ ++static int ubicom32lcd_get_power(struct lcd_device *ld) ++{ ++ struct ubicom32lcd_data *ud = (struct ubicom32lcd_data *)lcd_get_data(ld); ++ int vgh = gpio_get_value(ud->pdata->vgh_gpio); ++ if ((vgh && ud->pdata->vgh_polarity) || (!vgh && !ud->pdata->vgh_polarity)) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static struct lcd_ops ubicom32lcd_ops = { ++ .get_power = ubicom32lcd_get_power, ++ .set_power = ubicom32lcd_set_power, ++}; ++ ++/* ++ * ubicom32lcd_probe ++ */ ++static int ubicom32lcd_probe(struct platform_device *pdev) ++{ ++ const struct ubicom32lcd_platform_data *pdata = pdev->dev.platform_data; ++ struct ubicom32lcd_data *ud; ++ struct lcd_device *lcddev; ++ int retval; ++ ++ /* ++ * Check to see if we have any platform data, if we don't have a LCD to control ++ */ ++ if (!pdata) { ++ return -ENODEV; ++ } ++ ++ /* ++ * Allocate our private data ++ */ ++ ud = kzalloc(sizeof(struct ubicom32lcd_data), GFP_KERNEL); ++ if (!ud) { ++ return -ENOMEM; ++ } ++ ++ ud->pdata = pdata; ++ ++ /* ++ * Request our GPIOs ++ */ ++ retval = gpio_request(pdata->vgh_gpio, "vgh"); ++ if (retval) { ++ dev_err(&pdev->dev, "Failed to allocate vgh GPIO\n"); ++ goto fail_gpio; ++ } ++ ++ /* ++ * Register our lcd device ++ */ ++ lcddev = lcd_device_register(DRIVER_NAME, &pdev->dev, ud, &ubicom32lcd_ops); ++ if (IS_ERR(lcddev)) { ++ retval = PTR_ERR(lcddev); ++ goto fail; ++ } ++ ++ ud->lcddev = lcddev; ++ platform_set_drvdata(pdev, ud); ++ ++ ubicom32lcd_set_power(lcddev, FB_BLANK_UNBLANK); ++ ++ printk(KERN_INFO DRIVER_NAME ": LCD driver started\n"); ++ ++ return 0; ++ ++fail: ++ gpio_free(pdata->vgh_gpio); ++ ++fail_gpio: ++ platform_set_drvdata(pdev, NULL); ++ kfree(ud); ++ return retval; ++} ++ ++/* ++ * ubicom32lcd_remove ++ */ ++static int __exit ubicom32lcd_remove(struct platform_device *pdev) ++{ ++ struct ubicom32lcd_data *ud = platform_get_drvdata(pdev); ++ ++ lcd_device_unregister(ud->lcddev); ++ platform_set_drvdata(pdev, NULL); ++ kfree(ud); ++ ++ return 0; ++} ++ ++static struct platform_driver ubicom32lcd_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ ++ .remove = __exit_p(ubicom32lcd_remove), ++}; ++ ++/* ++ * ubicom32lcd_init ++ */ ++static int __init ubicom32lcd_init(void) ++{ ++ return platform_driver_probe(&ubicom32lcd_driver, ubicom32lcd_probe); ++} ++module_init(ubicom32lcd_init); ++ ++/* ++ * ubicom32lcd_exit ++ */ ++static void __exit ubicom32lcd_exit(void) ++{ ++ platform_driver_unregister(&ubicom32lcd_driver); ++} ++module_exit(ubicom32lcd_exit); ++ ++MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); ++MODULE_DESCRIPTION("Ubicom32 backlight driver"); ++MODULE_LICENSE("GPL"); +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -609,6 +609,25 @@ config FB_BFIN_T350MCQB + This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI + It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK. + ++config FB_UBICOM32 ++ tristate "Ubicom32 Frame Buffer driver" ++ depends on FB && UBICOM32 ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ select FONT_6x11 if FRAMEBUFFER_CONSOLE ++ help ++ This is the framebuffer device driver for the Ubicom32 architecture. ++ ++config FB_UBICOM32_VIRTUAL ++ tristate "Ubicom32 Virtual Frame Buffer driver" ++ depends on FB && UBICOM32 ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ select FONT_6x11 if FRAMEBUFFER_CONSOLE ++ help ++ This is a virtual framebuffer device driver for the Ubicom32 architecture. + + config FB_STI + tristate "HP STI frame buffer device support" +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -132,6 +132,7 @@ obj-$(CONFIG_FB_VGA16) += vga + obj-$(CONFIG_FB_OF) += offb.o + obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o + obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o ++obj-$(CONFIG_FB_UBICOM32) += ubicom32fb.o + + # the test framebuffer is last + obj-$(CONFIG_FB_VIRTUAL) += vfb.o +--- /dev/null ++++ b/drivers/video/ubicom32fb.c +@@ -0,0 +1,777 @@ ++/* ++ * drivers/video/ubicom32fb.c ++ * Ubicom32 frame buffer driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++/* ++ * This driver was based on skeletonfb.c, Skeleton for a frame buffer device by ++ * Geert Uytterhoeven. ++ */ ++ ++#include <linux/device.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/errno.h> ++#include <linux/string.h> ++#include <linux/mm.h> ++#include <linux/fb.h> ++#include <linux/init.h> ++#include <linux/dma-mapping.h> ++#include <linux/platform_device.h> ++#include <linux/device.h> ++#include <linux/uaccess.h> ++#include <linux/interrupt.h> ++ ++#include <asm/io.h> ++#include <asm/ip5000.h> ++#include <asm/vdc_tio.h> ++#include <asm/ubicom32fb.h> ++ ++#define DRIVER_NAME "ubicom32fb" ++#define DRIVER_DESCRIPTION "Ubicom32 frame buffer driver" ++ ++#define PALETTE_ENTRIES_NO 16 ++ ++/* ++ * Option variables ++ * ++ * vram_size: VRAM size in kilobytes, subject to alignment ++ */ ++static int vram_size = 0; ++module_param(vram_size, int, 0); ++MODULE_PARM_DESC(vram, "VRAM size, in kilobytes to allocate, should be at least the size of one screen, subject to alignment"); ++static int init_value = 0; ++module_param(init_value, int, 0); ++MODULE_PARM_DESC(init, "Initial value of the framebuffer (16-bit number)."); ++ ++/* ++ * fb_fix_screeninfo defines the non-changeable properties of the VDC, depending on what mode it is in. ++ */ ++static struct fb_fix_screeninfo ubicom32fb_fix = { ++ .id = "Ubicom32", ++ .type = FB_TYPE_PACKED_PIXELS, ++ .visual = FB_VISUAL_TRUECOLOR, ++ .accel = FB_ACCEL_NONE ++}; ++ ++/* ++ * Filled in at probe time when we find out what the hardware supports ++ */ ++static struct fb_var_screeninfo ubicom32fb_var; ++ ++/* ++ * Private data structure ++ */ ++struct ubicom32fb_drvdata { ++ struct fb_info *fbinfo; ++ bool cmap_alloc; ++ ++ /* ++ * The address of the framebuffer in memory ++ */ ++ void *fb; ++ void *fb_aligned; ++ ++ /* ++ * Total size of vram including alignment allowance ++ */ ++ u32 total_vram_size; ++ ++ /* ++ * Interrupt to set when changing registers ++ */ ++ u32 vp_int; ++ ++ /* ++ * Optional: Interrupt used by TIO to signal us ++ */ ++ u32 rx_int; ++ ++ /* ++ * Base address of the regs for VDC_TIO ++ */ ++ volatile struct vdc_tio_vp_regs *regs; ++ ++ /* ++ * non-zero if we are in yuv mode ++ */ ++ u8_t is_yuv; ++ ++ /* ++ * Fake palette of 16 colors ++ */ ++ u32 pseudo_palette[PALETTE_ENTRIES_NO]; ++ ++ /* ++ * Wait queue and lock used to block when we need to wait ++ * for something to happen. ++ */ ++ wait_queue_head_t waitq; ++ struct mutex lock; ++ ++}; ++ ++/* ++ * ubicom32fb_set_next_frame ++ * Sets the next frame buffer to display ++ * ++ * if sync is TRUE then this function will block until the hardware ++ * acknowledges the change ++ */ ++static inline void ubicom32fb_set_next_frame(struct ubicom32fb_drvdata *ud, void *fb, u8_t sync) ++{ ++ ud->regs->next_frame_flags = ud->is_yuv ? VDCTIO_NEXT_FRAME_FLAG_YUV : 0; ++ ud->regs->next_frame = (void *)((u32_t)fb | 1); ++ ++ /* ++ * If we have interrupts, then we can wait on it ++ */ ++ if (ud->rx_int != -1) { ++ DEFINE_WAIT(wait); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ud->lock, flags); ++ prepare_to_wait(&ud->waitq, &wait, TASK_INTERRUPTIBLE); ++ spin_unlock_irqrestore(&ud->lock, flags); ++ schedule(); ++ finish_wait(&ud->waitq, &wait); ++ return; ++ } ++ ++ /* ++ * No interrupt, we will just spin here ++ */ ++ while (sync && ((u32_t)ud->regs->next_frame & 1)); ++} ++ ++/* ++ * ubicom32fb_send_command ++ * Sends a command/data pair to the VDC ++ */ ++static inline void ubicom32fb_send_command(struct ubicom32fb_drvdata *ud, u16 command, u8_t block) ++{ ++ ud->regs->command = command; ++ ubicom32_set_interrupt(ud->vp_int); ++ while (block && ud->regs->command); ++} ++ ++/* ++ * ubicom32fb_ioctl ++ * Handles any ioctls sent to us ++ */ ++static int ubicom32fb_ioctl(struct fb_info *fbi, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct ubicom32fb_drvdata *ud = (struct ubicom32fb_drvdata *)fbi->par; ++ void __user *argp = (void __user *)arg; ++ int retval = -EFAULT; ++ ++ switch (cmd) { ++ case UBICOM32FB_IOCTL_SET_NEXT_FRAME_SYNC: ++ // check alignment, return -EINVAL if necessary ++ ubicom32fb_set_next_frame(ud, argp, 1); ++ retval = 0; ++ break; ++ ++ case UBICOM32FB_IOCTL_SET_NEXT_FRAME: ++ // check alignment, return -EINVAL if necessary ++ ubicom32fb_set_next_frame(ud, argp, 0); ++ retval = 0; ++ break; ++ ++ case UBICOM32FB_IOCTL_SET_MODE: ++ if (!(ud->regs->caps & VDCTIO_CAPS_SUPPORTS_SCALING)) { ++ break; ++ } else { ++ struct ubicom32fb_mode mode; ++ volatile struct vdc_tio_vp_regs *regs = ud->regs; ++ u32_t flags = 0; ++ ++ if (copy_from_user(&mode, argp, sizeof(mode))) { ++ break; ++ } ++ ++ regs->x_in = mode.width; ++ regs->y_in = mode.height; ++ regs->x_out = regs->xres; ++ regs->y_out = regs->yres; ++ if (mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_YUV_SCAN_ORDER) { ++ flags |= VDCTIO_SCALE_FLAG_YUV_SCAN_ORDER; ++ } ++ if (mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_YUV_BLOCK_ORDER) { ++ flags |= VDCTIO_SCALE_FLAG_YUV_BLOCK_ORDER; ++ } ++ ud->is_yuv = mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_YUV; ++ if (ud->is_yuv) { ++ flags |= VDCTIO_SCALE_FLAG_YUV; ++ } ++ if (mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_VRANGE_16_255) { ++ flags |= VDCTIO_SCALE_FLAG_VRANGE_16_255; ++ } ++ if (mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_VRANGE_0_255) { ++ flags |= VDCTIO_SCALE_FLAG_VRANGE_0_255; ++ } ++ if (mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_VSUB) { ++ flags |= VDCTIO_SCALE_FLAG_VSUB; ++ } ++ if (mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_HSUB_2_1) { ++ flags |= VDCTIO_SCALE_FLAG_HSUB_2_1; ++ } ++ if (mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_HSUB_1_1) { ++ flags |= VDCTIO_SCALE_FLAG_HSUB_1_1; ++ } ++ if (mode.flags & UBICOM32FB_IOCTL_SET_MODE_FLAG_SCALE_ENABLE) { ++ flags |= VDCTIO_SCALE_FLAG_ENABLE; ++ } ++ if (mode.next_frame) { ++ flags |= VDCTIO_SCALE_FLAG_SET_FRAME_BUFFER; ++ regs->next_frame = mode.next_frame; ++ } ++ ++ regs->scale_flags = flags; ++ ubicom32fb_send_command(ud, VDCTIO_COMMAND_SET_SCALE_MODE, 1); ++ retval = 0; ++ break; ++ } ++ ++ default: ++ retval = -ENOIOCTLCMD; ++ break; ++ } ++ ++ return retval; ++} ++ ++/* ++ * ubicom32fb_interrupt ++ * Called by the OS when the TIO has set the rx_int ++ */ ++static irqreturn_t ubicom32fb_interrupt(int vec, void *appdata) ++{ ++ struct ubicom32fb_drvdata *ud = (struct ubicom32fb_drvdata *)appdata; ++ ++ spin_lock(&ud->lock); ++ if (waitqueue_active(&ud->waitq)) { ++ wake_up(&ud->waitq); ++ } ++ spin_unlock(&ud->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * ubicom32fb_pan_display ++ * Pans the display to a given location. Supports only y direction panning. ++ */ ++static int ubicom32fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) ++{ ++ struct ubicom32fb_drvdata *ud = (struct ubicom32fb_drvdata *)fbi->par; ++ void *new_addr; ++ ++ /* ++ * Get the last y line that would be displayed. Since we don't support YWRAP, ++ * it must be less than our virtual y size. ++ */ ++ u32 lasty = var->yoffset + var->yres; ++ if (lasty > fbi->var.yres_virtual) { ++ /* ++ * We would fall off the end of our frame buffer if we panned here. ++ */ ++ return -EINVAL; ++ } ++ ++ if (var->xoffset) { ++ /* ++ * We don't support panning in the x direction ++ */ ++ return -EINVAL; ++ } ++ ++ /* ++ * Everything looks sane, go ahead and pan ++ * ++ * We have to calculate a new address for the VDC to look at ++ */ ++ new_addr = ud->fb_aligned + (var->yoffset * fbi->fix.line_length); ++ ++ /* ++ * Send down the command. The buffer will switch at the next vertical blank ++ */ ++ ubicom32fb_set_next_frame(ud, (void *)new_addr, 0); ++ ++ return 0; ++} ++ ++/* ++ * ubicom32fb_setcolreg ++ * Sets a color in our virtual palette ++ */ ++static int ubicom32fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fbi) ++{ ++ u32 *palette = fbi->pseudo_palette; ++ ++ if (regno >= PALETTE_ENTRIES_NO) { ++ return -EINVAL; ++ } ++ ++ /* ++ * We only use 8 bits from each color ++ */ ++ red >>= 8; ++ green >>= 8; ++ blue >>= 8; ++ ++ /* ++ * Convert any grayscale values ++ */ ++ if (fbi->var.grayscale) { ++ u16 gray = red + green + blue; ++ gray += (gray >> 2) + (gray >> 3) - (gray >> 7); ++ gray >>= 2; ++ if (gray > 255) { ++ gray = 255; ++ } ++ red = gray; ++ blue = gray; ++ green = gray; ++ } ++ ++ palette[regno] = (red << fbi->var.red.offset) | (green << fbi->var.green.offset) | ++ (blue << fbi->var.blue.offset); ++ ++ return 0; ++} ++ ++/* ++ * ubicom32fb_mmap ++ */ ++static int ubicom32fb_mmap(struct fb_info *info, struct vm_area_struct *vma) ++{ ++ struct ubicom32fb_drvdata *drvdata = (struct ubicom32fb_drvdata *)info->par; ++ ++ vma->vm_start = (unsigned long)(drvdata->fb_aligned); ++ ++ vma->vm_end = vma->vm_start + info->fix.smem_len; ++ ++ /* For those who don't understand how mmap works, go read ++ * Documentation/nommu-mmap.txt. ++ * For those that do, you will know that the VM_MAYSHARE flag ++ * must be set in the vma->vm_flags structure on noMMU ++ * Other flags can be set, and are documented in ++ * include/linux/mm.h ++ */ ++ ++ vma->vm_flags |= VM_MAYSHARE | VM_SHARED; ++ ++ return 0; ++} ++ ++/* ++ * ubicom32fb_blank ++ */ ++static int ubicom32fb_blank(int blank_mode, struct fb_info *fbi) ++{ ++ return 0; ++#if 0 ++ struct ubicom32fb_drvdata *drvdata = to_ubicom32fb_drvdata(fbi); ++ ++ switch (blank_mode) { ++ case FB_BLANK_UNBLANK: ++ /* turn on panel */ ++ ubicom32fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default); ++ break; ++ ++ case FB_BLANK_NORMAL: ++ case FB_BLANK_VSYNC_SUSPEND: ++ case FB_BLANK_HSYNC_SUSPEND: ++ case FB_BLANK_POWERDOWN: ++ /* turn off panel */ ++ ubicom32fb_out_be32(drvdata, REG_CTRL, 0); ++ default: ++ break; ++ ++ } ++ return 0; /* success */ ++#endif ++} ++ ++static struct fb_ops ubicom32fb_ops = ++{ ++ .owner = THIS_MODULE, ++ .fb_pan_display = ubicom32fb_pan_display, ++ .fb_setcolreg = ubicom32fb_setcolreg, ++ .fb_blank = ubicom32fb_blank, ++ .fb_mmap = ubicom32fb_mmap, ++ .fb_ioctl = ubicom32fb_ioctl, ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++}; ++ ++/* ++ * ubicom32fb_release ++ */ ++static int ubicom32fb_release(struct device *dev) ++{ ++ struct ubicom32fb_drvdata *ud = dev_get_drvdata(dev); ++ ++#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) ++ //ubicom32fb_blank(VESA_POWERDOWN, &drvdata->info); ++#endif ++ ++ unregister_framebuffer(ud->fbinfo); ++ ++ if (ud->cmap_alloc) { ++ fb_dealloc_cmap(&ud->fbinfo->cmap); ++ } ++ ++ if (ud->fb) { ++ kfree(ud->fb); ++ } ++ ++ if (ud->rx_int != -1) { ++ free_irq(ud->rx_int, ud); ++ } ++ ++ /* ++ * Turn off the display ++ */ ++ //ubicom32fb_out_be32(drvdata, REG_CTRL, 0); ++ //iounmap(drvdata->regs); ++ ++ framebuffer_release(ud->fbinfo); ++ dev_set_drvdata(dev, NULL); ++ ++ return 0; ++} ++ ++/* ++ * ubicom32fb_platform_probe ++ */ ++static int __init ubicom32fb_platform_probe(struct platform_device *pdev) ++{ ++ struct ubicom32fb_drvdata *ud; ++ struct resource *irq_resource_rx; ++ struct resource *irq_resource_tx; ++ struct resource *mem_resource; ++ struct fb_info *fbinfo; ++ int rc; ++ size_t fbsize; ++ struct device *dev = &pdev->dev; ++ int offset; ++ struct vdc_tio_vp_regs *regs; ++ ++ /* ++ * Get our resources ++ */ ++ irq_resource_tx = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!irq_resource_tx) { ++ dev_err(dev, "No tx IRQ resource assigned\n"); ++ return -ENODEV; ++ } ++ ++ irq_resource_rx = platform_get_resource(pdev, IORESOURCE_IRQ, 1); ++ if (!irq_resource_rx) { ++ dev_err(dev, "No rx IRQ resource assigned\n"); ++ return -ENODEV; ++ } ++ ++ mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!mem_resource || !mem_resource->start) { ++ dev_err(dev, "No mem resource assigned\n"); ++ return -ENODEV; ++ } ++ regs = (struct vdc_tio_vp_regs *)mem_resource->start; ++ if (regs->version != VDCTIO_VP_VERSION) { ++ dev_err(dev, "VDCTIO is not compatible with this driver tio:%x drv:%x\n", ++ regs->version, VDCTIO_VP_VERSION); ++ return -ENODEV; ++ } ++ ++ /* ++ * This is the minimum VRAM size ++ */ ++ fbsize = regs->xres * regs->yres * (regs->bpp / 8); ++ if (!vram_size) { ++ vram_size = (fbsize + 1023) / 1024; ++ } else { ++ if (fbsize > (vram_size * 1024)) { ++ dev_err(dev, "Not enough VRAM for display, need >= %u bytes\n", fbsize); ++ return -ENOMEM; // should be ebadparam? ++ } ++ } ++ ++ /* ++ * Allocate the framebuffer instance + our private data ++ */ ++ fbinfo = framebuffer_alloc(sizeof(struct ubicom32fb_drvdata), &pdev->dev); ++ if (!fbinfo) { ++ dev_err(dev, "Not enough memory to allocate instance.\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * Fill in our private data. ++ */ ++ ud = (struct ubicom32fb_drvdata *)fbinfo->par; ++ ud->fbinfo = fbinfo; ++ ud->regs = (struct vdc_tio_vp_regs *)(mem_resource->start); ++ dev_set_drvdata(dev, ud); ++ ++ ud->vp_int = irq_resource_tx->start; ++ ++ /* ++ * If we were provided an rx_irq then we need to init the appropriate ++ * queues, locks, and functions. ++ */ ++ ud->rx_int = -1; ++ if (irq_resource_rx->start != DEVTREE_IRQ_NONE) { ++ init_waitqueue_head(&ud->waitq); ++ mutex_init(&ud->lock); ++ if (request_irq(ud->rx_int, ubicom32fb_interrupt, IRQF_SHARED, "ubicom32fb_rx", ud)) { ++ dev_err(dev, "Couldn't request rx IRQ\n"); ++ rc = -ENOMEM; ++ goto fail; ++ } ++ ud->rx_int = irq_resource_rx->start; ++ } ++ ++ /* ++ * Allocate and align the requested amount of VRAM ++ */ ++ ud->total_vram_size = (vram_size * 1024) + regs->fb_align; ++ ud->fb = kmalloc(ud->total_vram_size, GFP_KERNEL); ++ if (ud->fb == NULL) { ++ dev_err(dev, "Couldn't allocate VRAM\n"); ++ rc = -ENOMEM; ++ goto fail; ++ } ++ ++ offset = (u32_t)ud->fb & (regs->fb_align - 1); ++ if (!offset) { ++ ud->fb_aligned = ud->fb; ++ } else { ++ offset = regs->fb_align - offset; ++ ud->fb_aligned = ud->fb + offset; ++ } ++ ++ /* ++ * Clear the entire frame buffer ++ */ ++ if (!init_value) { ++ memset(ud->fb_aligned, 0, vram_size * 1024); ++ } else { ++ unsigned short *p = ud->fb_aligned; ++ int i; ++ for (i = 0; i < ((vram_size * 1024) / sizeof(u16_t)); i++) { ++ *p++ = init_value; ++ } ++ } ++ ++ /* ++ * Fill in the fb_var_screeninfo structure ++ */ ++ memset(&ubicom32fb_var, 0, sizeof(ubicom32fb_var)); ++ ubicom32fb_var.bits_per_pixel = regs->bpp; ++ ubicom32fb_var.red.offset = regs->rshift; ++ ubicom32fb_var.green.offset = regs->gshift; ++ ubicom32fb_var.blue.offset = regs->bshift; ++ ubicom32fb_var.red.length = regs->rbits; ++ ubicom32fb_var.green.length = regs->gbits; ++ ubicom32fb_var.blue.length = regs->bbits; ++ ubicom32fb_var.activate = FB_ACTIVATE_NOW; ++ ++#if 0 ++ /* ++ * Turn on the display ++ */ ++ ud->reg_ctrl_default = REG_CTRL_ENABLE; ++ if (regs->rotate_screen) ++ ud->reg_ctrl_default |= REG_CTRL_ROTATE; ++ ubicom32fb_out_be32(ud, REG_CTRL, ud->reg_ctrl_default); ++#endif ++ ++ /* ++ * Fill in the fb_info structure ++ */ ++ ud->fbinfo->device = dev; ++ ud->fbinfo->screen_base = (void *)ud->fb_aligned; ++ ud->fbinfo->fbops = &ubicom32fb_ops; ++ ud->fbinfo->fix = ubicom32fb_fix; ++ ud->fbinfo->fix.smem_start = (u32)ud->fb_aligned; ++ ud->fbinfo->fix.smem_len = fbsize; ++ ud->fbinfo->fix.line_length = regs->xres * (regs->bpp / 8); ++ ++ /* ++ * We support panning in the y direction only ++ */ ++ ud->fbinfo->fix.xpanstep = 0; ++ ud->fbinfo->fix.ypanstep = 1; ++ ++ ud->fbinfo->pseudo_palette = ud->pseudo_palette; ++ ud->fbinfo->flags = FBINFO_DEFAULT; ++ ud->fbinfo->var = ubicom32fb_var; ++ ud->fbinfo->var.xres = regs->xres; ++ ud->fbinfo->var.yres = regs->yres; ++ ++ /* ++ * We cannot pan in the X direction, so xres_virtual is regs->xres ++ * We can pan in the Y direction, so yres_virtual is vram_size / ud->fbinfo->fix.line_length ++ */ ++ ud->fbinfo->var.xres_virtual = regs->xres; ++ ud->fbinfo->var.yres_virtual = (vram_size * 1024) / ud->fbinfo->fix.line_length; ++ ++ //ud->fbinfo->var.height = regs->height_mm; ++ //ud->fbinfo->var.width = regs->width_mm; ++ ++ /* ++ * Allocate a color map ++ */ ++ rc = fb_alloc_cmap(&ud->fbinfo->cmap, PALETTE_ENTRIES_NO, 0); ++ if (rc) { ++ dev_err(dev, "Fail to allocate colormap (%d entries)\n", ++ PALETTE_ENTRIES_NO); ++ goto fail; ++ } ++ ud->cmap_alloc = true; ++ ++ /* ++ * Register new frame buffer ++ */ ++ rc = register_framebuffer(ud->fbinfo); ++ if (rc) { ++ dev_err(dev, "Could not register frame buffer\n"); ++ goto fail; ++ } ++ ++ /* ++ * Start up the VDC ++ */ ++ ud->regs->next_frame = ud->fb; ++ ubicom32fb_send_command(ud, VDCTIO_COMMAND_START, 0); ++ ++ /* ++ * Tell the log we are here ++ */ ++ dev_info(dev, "fbaddr=%p align=%p, size=%uKB screen(%ux%u) virt(%ux%u), regs=%p irqtx=%u irqrx=%u\n", ++ ud->fb, ud->fb_aligned, vram_size, ud->fbinfo->var.xres, ud->fbinfo->var.yres, ++ ud->fbinfo->var.xres_virtual, ud->fbinfo->var.yres_virtual, ud->regs, ++ irq_resource_tx->start, irq_resource_rx->start); ++ ++ /* ++ * Success ++ */ ++ return 0; ++ ++fail: ++ ubicom32fb_release(dev); ++ return rc; ++} ++ ++/* ++ * ubicom32fb_platform_remove ++ */ ++static int ubicom32fb_platform_remove(struct platform_device *pdev) ++{ ++ dev_info(&(pdev->dev), "Ubicom32 FB Driver Remove\n"); ++ return ubicom32fb_release(&pdev->dev); ++} ++ ++static struct platform_driver ubicom32fb_platform_driver = { ++ .probe = ubicom32fb_platform_probe, ++ .remove = ubicom32fb_platform_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++#ifndef MODULE ++/* ++ * ubicom32fb_setup ++ * Process kernel boot options ++ */ ++static int __init ubicom32fb_setup(char *options) ++{ ++ char *this_opt; ++ ++ if (!options || !*options) { ++ return 0; ++ } ++ ++ while ((this_opt = strsep(&options, ",")) != NULL) { ++ if (!*this_opt) { ++ continue; ++ } ++ ++ if (!strncmp(this_opt, "init_value=", 10)) { ++ init_value = simple_strtoul(this_opt + 11, NULL, 0); ++ continue; ++ } ++ ++ if (!strncmp(this_opt, "vram_size=", 10)) { ++ vram_size = simple_strtoul(this_opt + 10, NULL, 0); ++ continue; ++ } ++ } ++ return 0; ++} ++#endif /* MODULE */ ++ ++/* ++ * ubicom32fb_init ++ */ ++static int __devinit ubicom32fb_init(void) ++{ ++#ifndef MODULE ++ /* ++ * Get kernel boot options (in 'video=ubicom32fb:<options>') ++ */ ++ char *option = NULL; ++ ++ if (fb_get_options(DRIVER_NAME, &option)) { ++ return -ENODEV; ++ } ++ ubicom32fb_setup(option); ++#endif /* MODULE */ ++ ++ return platform_driver_register(&ubicom32fb_platform_driver); ++} ++module_init(ubicom32fb_init); ++ ++/* ++ * ubicom32fb_exit ++ */ ++static void __exit ubicom32fb_exit(void) ++{ ++ platform_driver_unregister(&ubicom32fb_platform_driver); ++} ++module_exit(ubicom32fb_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); ++MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +--- /dev/null ++++ b/drivers/video/ubicom32vfb.c +@@ -0,0 +1,492 @@ ++/* ++ * drivers/video/ubicom32vfb.c ++ * Ubicom32 virtual frame buffer driver ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++/* ++ * This driver was based on skeletonfb.c, Skeleton for a frame buffer device by ++ * Geert Uytterhoeven. ++ */ ++ ++#include <linux/device.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/errno.h> ++#include <linux/string.h> ++#include <linux/mm.h> ++#include <linux/fb.h> ++#include <linux/init.h> ++#include <linux/dma-mapping.h> ++#include <linux/platform_device.h> ++#include <linux/device.h> ++#include <linux/uaccess.h> ++ ++#define DRIVER_NAME "ubicom32vfb" ++#define DRIVER_DESCRIPTION "Ubicom32 virtual frame buffer driver" ++ ++#define PALETTE_ENTRIES_NO 16 ++ ++/* ++ * Option variables ++ * ++ * vram_size: VRAM size in kilobytes, subject to alignment ++ */ ++static int vram_size = 0; ++module_param(vram_size, int, 0); ++MODULE_PARM_DESC(vram_size, "VRAM size, in kilobytes to allocate, should be at least the size of one screen, subject to alignment"); ++ ++static int xres = 320; ++module_param(xres, int, 0); ++MODULE_PARM_DESC(xres, "x (horizontal) resolution"); ++ ++static int yres = 240; ++module_param(yres, int, 0); ++MODULE_PARM_DESC(yres, "y (vertical) resolution"); ++ ++static int bgr = 0; ++module_param(bgr, int, 0); ++MODULE_PARM_DESC(bgr, "display is BGR (Blue is MSB)"); ++ ++/* ++ * Buffer alignment, must not be 0 ++ */ ++#define UBICOM32VFB_ALIGNMENT 4 ++ ++/* ++ * fb_fix_screeninfo defines the non-changeable properties of the VDC, depending on what mode it is in. ++ */ ++static struct fb_fix_screeninfo ubicom32vfb_fix = { ++ .id = "Ubicom32", ++ .type = FB_TYPE_PACKED_PIXELS, ++ .visual = FB_VISUAL_TRUECOLOR, ++ .accel = FB_ACCEL_NONE ++}; ++ ++/* ++ * Filled in at probe time when we find out what the hardware supports ++ */ ++static struct fb_var_screeninfo ubicom32vfb_var; ++ ++/* ++ * Private data structure ++ */ ++struct ubicom32vfb_drvdata { ++ struct fb_info *fbinfo; ++ bool cmap_alloc; ++ ++ /* ++ * The address of the framebuffer in memory ++ */ ++ void *fb; ++ void *fb_aligned; ++ ++ /* ++ * Total size of vram including alignment allowance ++ */ ++ u32 total_vram_size; ++ ++ /* ++ * Fake palette of 16 colors ++ */ ++ u32 pseudo_palette[PALETTE_ENTRIES_NO]; ++}; ++ ++/* ++ * ubicom32vfb_pan_display ++ * Pans the display to a given location. Supports only y direction panning. ++ */ ++static int ubicom32vfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) ++{ ++ struct ubicom32vfb_drvdata *ud = (struct ubicom32vfb_drvdata *)fbi->par; ++ void *new_addr; ++ ++ /* ++ * Get the last y line that would be displayed. Since we don't support YWRAP, ++ * it must be less than our virtual y size. ++ */ ++ u32 lasty = var->yoffset + var->yres; ++ if (lasty > fbi->var.yres_virtual) { ++ /* ++ * We would fall off the end of our frame buffer if we panned here. ++ */ ++ return -EINVAL; ++ } ++ ++ if (var->xoffset) { ++ /* ++ * We don't support panning in the x direction ++ */ ++ return -EINVAL; ++ } ++ ++ /* ++ * Everything looks sane, go ahead and pan ++ * ++ * We have to calculate a new address for the VDC to look at ++ */ ++ new_addr = ud->fb_aligned + (var->yoffset * fbi->fix.line_length); ++ ++ return 0; ++} ++ ++/* ++ * ubicom32vfb_setcolreg ++ * Sets a color in our virtual palette ++ */ ++static int ubicom32vfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fbi) ++{ ++ u32 *palette = fbi->pseudo_palette; ++ ++ if (regno >= PALETTE_ENTRIES_NO) { ++ return -EINVAL; ++ } ++ ++ /* ++ * We only use 8 bits from each color ++ */ ++ red >>= 8; ++ green >>= 8; ++ blue >>= 8; ++ ++ /* ++ * Convert any grayscale values ++ */ ++ if (fbi->var.grayscale) { ++ u16 gray = red + green + blue; ++ gray += (gray >> 2) + (gray >> 3) - (gray >> 7); ++ gray >>= 2; ++ if (gray > 255) { ++ gray = 255; ++ } ++ red = gray; ++ blue = gray; ++ green = gray; ++ } ++ ++ palette[regno] = (red << fbi->var.red.offset) | (green << fbi->var.green.offset) | ++ (blue << fbi->var.blue.offset); ++ ++ return 0; ++} ++ ++/* ++ * ubicom32vfb_mmap ++ */ ++static int ubicom32vfb_mmap(struct fb_info *info, struct vm_area_struct *vma) ++{ ++ struct ubicom32vfb_drvdata *drvdata = (struct ubicom32vfb_drvdata *)info->par; ++ ++ vma->vm_start = (unsigned long)(drvdata->fb_aligned); ++ ++ vma->vm_end = vma->vm_start + info->fix.smem_len; ++ ++ /* For those who don't understand how mmap works, go read ++ * Documentation/nommu-mmap.txt. ++ * For those that do, you will know that the VM_MAYSHARE flag ++ * must be set in the vma->vm_flags structure on noMMU ++ * Other flags can be set, and are documented in ++ * include/linux/mm.h ++ */ ++ ++ vma->vm_flags |= VM_MAYSHARE | VM_SHARED; ++ ++ return 0; ++} ++ ++static struct fb_ops ubicom32vfb_ops = ++{ ++ .owner = THIS_MODULE, ++ .fb_pan_display = ubicom32vfb_pan_display, ++ .fb_setcolreg = ubicom32vfb_setcolreg, ++ .fb_mmap = ubicom32vfb_mmap, ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++}; ++ ++/* ++ * ubicom32vfb_release ++ */ ++static int ubicom32vfb_release(struct device *dev) ++{ ++ struct ubicom32vfb_drvdata *ud = dev_get_drvdata(dev); ++ ++ unregister_framebuffer(ud->fbinfo); ++ ++ if (ud->cmap_alloc) { ++ fb_dealloc_cmap(&ud->fbinfo->cmap); ++ } ++ ++ if (ud->fb) { ++ kfree(ud->fb); ++ } ++ ++ framebuffer_release(ud->fbinfo); ++ dev_set_drvdata(dev, NULL); ++ ++ return 0; ++} ++ ++/* ++ * ubicom32vfb_platform_probe ++ */ ++static int __init ubicom32vfb_platform_probe(struct platform_device *pdev) ++{ ++ struct ubicom32vfb_drvdata *ud; ++ struct fb_info *fbinfo; ++ int rc; ++ size_t fbsize; ++ struct device *dev = &pdev->dev; ++ int offset; ++ ++ /* ++ * This is the minimum VRAM size ++ */ ++ fbsize = xres * yres * 2; ++ if (!vram_size) { ++ vram_size = (fbsize + 1023) / 1024; ++ } else { ++ if (fbsize > (vram_size * 1024)) { ++ dev_err(dev, "Not enough VRAM for display, need >= %u bytes\n", fbsize); ++ return -ENOMEM; // should be ebadparam? ++ } ++ } ++ ++ /* ++ * Allocate the framebuffer instance + our private data ++ */ ++ fbinfo = framebuffer_alloc(sizeof(struct ubicom32vfb_drvdata), &pdev->dev); ++ if (!fbinfo) { ++ dev_err(dev, "Not enough memory to allocate instance.\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * Fill in our private data. ++ */ ++ ud = (struct ubicom32vfb_drvdata *)fbinfo->par; ++ ud->fbinfo = fbinfo; ++ dev_set_drvdata(dev, ud); ++ ++ /* ++ * Allocate and align the requested amount of VRAM ++ */ ++ ud->total_vram_size = (vram_size * 1024) + UBICOM32VFB_ALIGNMENT; ++ ud->fb = kmalloc(ud->total_vram_size, GFP_KERNEL); ++ if (ud->fb == NULL) { ++ dev_err(dev, "Couldn't allocate VRAM\n"); ++ rc = -ENOMEM; ++ goto fail; ++ } ++ ++ offset = (u32_t)ud->fb & (UBICOM32VFB_ALIGNMENT - 1); ++ if (!offset) { ++ ud->fb_aligned = ud->fb; ++ } else { ++ offset = UBICOM32VFB_ALIGNMENT - offset; ++ ud->fb_aligned = ud->fb + offset; ++ } ++ ++ /* ++ * Clear the entire frame buffer ++ */ ++ memset(ud->fb_aligned, 0, vram_size * 1024); ++ ++ /* ++ * Fill in the fb_var_screeninfo structure ++ */ ++ memset(&ubicom32vfb_var, 0, sizeof(ubicom32vfb_var)); ++ ubicom32vfb_var.bits_per_pixel = 16; ++ ubicom32vfb_var.red.length = 5; ++ ubicom32vfb_var.green.length = 6; ++ ubicom32vfb_var.green.offset = 5; ++ ubicom32vfb_var.blue.length = 5; ++ ubicom32vfb_var.activate = FB_ACTIVATE_NOW; ++ ++ if (bgr) { ++ ubicom32vfb_var.red.offset = 0; ++ ubicom32vfb_var.blue.offset = 11; ++ } else { ++ ubicom32vfb_var.red.offset = 11; ++ ubicom32vfb_var.blue.offset = 0; ++ } ++ ++ /* ++ * Fill in the fb_info structure ++ */ ++ ud->fbinfo->device = dev; ++ ud->fbinfo->screen_base = (void *)ud->fb_aligned; ++ ud->fbinfo->fbops = &ubicom32vfb_ops; ++ ud->fbinfo->fix = ubicom32vfb_fix; ++ ud->fbinfo->fix.smem_start = (u32)ud->fb_aligned; ++ ud->fbinfo->fix.smem_len = fbsize; ++ ud->fbinfo->fix.line_length = xres * 2; ++ ++ /* ++ * We support panning in the y direction only ++ */ ++ ud->fbinfo->fix.xpanstep = 0; ++ ud->fbinfo->fix.ypanstep = 1; ++ ++ ud->fbinfo->pseudo_palette = ud->pseudo_palette; ++ ud->fbinfo->flags = FBINFO_DEFAULT; ++ ud->fbinfo->var = ubicom32vfb_var; ++ ud->fbinfo->var.xres = xres; ++ ud->fbinfo->var.yres = yres; ++ ++ /* ++ * We cannot pan in the X direction, so xres_virtual is xres ++ * We can pan in the Y direction, so yres_virtual is vram_size / ud->fbinfo->fix.line_length ++ */ ++ ud->fbinfo->var.xres_virtual = xres; ++ ud->fbinfo->var.yres_virtual = (vram_size * 1024) / ud->fbinfo->fix.line_length; ++ ++ /* ++ * Allocate a color map ++ */ ++ rc = fb_alloc_cmap(&ud->fbinfo->cmap, PALETTE_ENTRIES_NO, 0); ++ if (rc) { ++ dev_err(dev, "Fail to allocate colormap (%d entries)\n", ++ PALETTE_ENTRIES_NO); ++ goto fail; ++ } ++ ud->cmap_alloc = true; ++ ++ /* ++ * Register new frame buffer ++ */ ++ rc = register_framebuffer(ud->fbinfo); ++ if (rc) { ++ dev_err(dev, "Could not register frame buffer\n"); ++ goto fail; ++ } ++ ++ /* ++ * Tell the log we are here ++ */ ++ dev_info(dev, "fbaddr=%p align=%p, size=%uKB screen(%ux%u) virt(%ux%u)\n", ++ ud->fb, ud->fb_aligned, vram_size, ud->fbinfo->var.xres, ud->fbinfo->var.yres, ++ ud->fbinfo->var.xres_virtual, ud->fbinfo->var.yres_virtual); ++ ++ /* ++ * Success ++ */ ++ return 0; ++ ++fail: ++ ubicom32vfb_release(dev); ++ return rc; ++} ++ ++/* ++ * ubicom32vfb_platform_remove ++ */ ++static int ubicom32vfb_platform_remove(struct platform_device *pdev) ++{ ++ dev_info(&(pdev->dev), "Ubicom32 FB Driver Remove\n"); ++ return ubicom32vfb_release(&pdev->dev); ++} ++ ++static struct platform_driver ubicom32vfb_platform_driver = { ++ .probe = ubicom32vfb_platform_probe, ++ .remove = ubicom32vfb_platform_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++#ifndef MODULE ++/* ++ * ubicom32vfb_setup ++ * Process kernel boot options ++ */ ++static int __init ubicom32vfb_setup(char *options) ++{ ++ char *this_opt; ++ ++ if (!options || !*options) { ++ return 0; ++ } ++ ++ while ((this_opt = strsep(&options, ",")) != NULL) { ++ if (!*this_opt) { ++ continue; ++ } ++ ++ if (!strncmp(this_opt, "vram_size=", 10)) { ++ vram_size = simple_strtoul(this_opt + 10, NULL, 0); ++ continue; ++ } ++ ++ if (!strncmp(this_opt, "bgr=", 4)) { ++ bgr = simple_strtoul(this_opt + 4, NULL, 0); ++ continue; ++ } ++ ++ if (!strncmp(this_opt, "xres=", 5)) { ++ xres = simple_strtoul(this_opt + 5, NULL, 0); ++ continue; ++ } ++ ++ if (!strncmp(this_opt, "yres=", 5)) { ++ yres = simple_strtoul(this_opt + 5, NULL, 0); ++ continue; ++ } ++ } ++ return 0; ++} ++#endif /* MODULE */ ++ ++/* ++ * ubicom32vfb_init ++ */ ++static int __devinit ubicom32vfb_init(void) ++{ ++#ifndef MODULE ++ /* ++ * Get kernel boot options (in 'video=ubicom32vfb:<options>') ++ */ ++ char *option = NULL; ++ ++ if (fb_get_options(DRIVER_NAME, &option)) { ++ return -ENODEV; ++ } ++ ubicom32vfb_setup(option); ++#endif /* MODULE */ ++ ++ return platform_driver_register(&ubicom32vfb_platform_driver); ++} ++module_init(ubicom32vfb_init); ++ ++/* ++ * ubicom32vfb_exit ++ */ ++static void __exit ubicom32vfb_exit(void) ++{ ++ platform_driver_unregister(&ubicom32vfb_platform_driver); ++} ++module_exit(ubicom32vfb_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); ++MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -856,6 +856,19 @@ config WATCHDOG_RIO + machines. The watchdog timeout period is normally one minute but + can be changed with a boot-time parameter. + ++# Ubicom32 ++ ++config UBI32_WDT ++ tristate "Ubicom32 Hardware Watchdog support" ++ depends on UBICOM32 ++ ---help--- ++ If you say yes here you will get support for the Ubicom32 On-Chip ++ Watchdog Timer. If you have one of these processors and wish to ++ have watchdog support enabled, say Y, otherwise say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ubi32_wdt. ++ + # XTENSA Architecture + + # +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -130,6 +130,9 @@ obj-$(CONFIG_SH_WDT) += shwdt.o + obj-$(CONFIG_WATCHDOG_RIO) += riowd.o + obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o + ++# Ubicom32 Architecture ++obj-$(CONFIG_UBI32_WDT) += ubi32_wdt.o ++ + # XTENSA Architecture + + # Architecture Independant +--- /dev/null ++++ b/drivers/watchdog/ubi32_wdt.c +@@ -0,0 +1,630 @@ ++/* ++ * drivers/watchdog/ubi32_wdt.c ++ * Ubicom32 Watchdog Driver ++ * ++ * Originally based on softdog.c ++ * Copyright 2006-2007 Analog Devices Inc. ++ * Copyright 2006-2007 Michele d'Amico ++ * Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk> ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/types.h> ++#include <linux/timer.h> ++#include <linux/miscdevice.h> ++#include <linux/watchdog.h> ++#include <linux/fs.h> ++#include <linux/notifier.h> ++#include <linux/reboot.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/uaccess.h> ++#include <asm/ip5000.h> ++ ++#define WATCHDOG_NAME "ubi32-wdt" ++#define PFX WATCHDOG_NAME ": " ++ ++#define OSC1_FREQ 12000000 ++#define WATCHDOG_SEC_TO_CYC(x) (OSC1_FREQ * (x)) ++#define WATCHDOG_MAX_SEC (0xffffffff / OSC1_FREQ) ++ ++#define MIN_PROCESSOR_ADDRESS 0x03000000 ++ ++static DEFINE_SPINLOCK(ubi32_wdt_spinlock); ++ ++#define WATCHDOG_TIMEOUT 20 ++ ++#if defined(CONFIG_WATCHDOG_NOWAYOUT) ++#define WATCHDOG_NOWAYOUT 1 ++#else ++#define WATCHDOG_NOWAYOUT 0 ++#endif ++ ++static unsigned int timeout = WATCHDOG_TIMEOUT; ++static int nowayout = WATCHDOG_NOWAYOUT; ++static struct watchdog_info ubi32_wdt_info; ++static unsigned long open_check; ++static char expect_close; ++ ++#if !defined(CONFIG_SMP) ++#define UBI32_WDT_LOCK(lock, flags) local_irq_save(flags) ++#define UBI32_WDT_UNLOCK(lock, flags) local_irq_restore(flags) ++#define UBI32_WDT_LOCK_CHECK() ++#else ++#define UBI32_WDT_LOCK(lock, flags) spin_lock_irqsave((lock), (flags)); ++#define UBI32_WDT_UNLOCK(lock, flags) spin_unlock_irqrestore((lock), (flags)); ++#define UBI32_WDT_LOCK_CHECK() BUG_ON(!spin_is_locked(&ubi32_wdt_spinlock)); ++#endif ++ ++/* ++ * ubi32_wdt_remaining() ++ * Return the approximate number of seconds remaining ++ */ ++static int ubi32_wdt_remaining(void) ++{ ++ int compare; ++ int curr; ++ ++ UBI32_WDT_LOCK_CHECK(); ++ ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, TIMER_TKEYVAL); ++ compare = ubicom32_read_reg(&UBICOM32_IO_TIMER->wdcom); ++ curr = ubicom32_read_reg(&UBICOM32_IO_TIMER->mptval); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, 0); ++ return (compare - curr) / OSC1_FREQ; ++ ++} ++ ++/* ++ * ubi32_wdt_keepalive() ++ * Keep the Userspace Watchdog Alive ++ * ++ * The Userspace watchdog got a KeepAlive: schedule the next timeout. ++ */ ++static int ubi32_wdt_keepalive(void) ++{ ++ UBI32_WDT_LOCK_CHECK(); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, TIMER_TKEYVAL); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->wdcom, ++ ubicom32_read_reg(&UBICOM32_IO_TIMER->mptval) ++ + WATCHDOG_SEC_TO_CYC(timeout)); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, 0); ++ return 0; ++} ++ ++/* ++ * ubi32_wdt_stop() ++ * Stop the on-chip Watchdog ++ */ ++static int ubi32_wdt_stop(void) ++{ ++ UBI32_WDT_LOCK_CHECK(); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, TIMER_TKEYVAL); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->wdcfg, TIMER_WATCHDOG_DISABLE); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, 0); ++ return 0; ++} ++ ++/* ++ * ubi32_wdt_start() ++ * Start the on-chip Watchdog ++ */ ++static int ubi32_wdt_start(void) ++{ ++ UBI32_WDT_LOCK_CHECK(); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, TIMER_TKEYVAL); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->wdcom, ++ ubicom32_read_reg(&UBICOM32_IO_TIMER->mptval) ++ + WATCHDOG_SEC_TO_CYC(timeout)); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->wdcfg, ~TIMER_WATCHDOG_DISABLE); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, 0); ++ return 0; ++} ++ ++/* ++ * ubi32_wdt_running() ++ * Return true if the watchdog is configured ++ */ ++static int ubi32_wdt_running(void) ++{ ++ int enabled; ++ ++ UBI32_WDT_LOCK_CHECK(); ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, TIMER_TKEYVAL); ++ enabled = ubicom32_read_reg(&UBICOM32_IO_TIMER->wdcfg) == ~TIMER_WATCHDOG_DISABLE; ++ ubicom32_write_reg(&UBICOM32_IO_TIMER->tkey, 0); ++ return enabled; ++} ++ ++/* ++ * ubi32_wdt_set_timeout() ++ * Set the Userspace Watchdog timeout ++ * ++ * - @t: new timeout value (in seconds) ++ */ ++static int ubi32_wdt_set_timeout(unsigned long t) ++{ ++ UBI32_WDT_LOCK_CHECK(); ++ ++ if (t > WATCHDOG_MAX_SEC) { ++ printk(KERN_WARNING PFX "request to large: %ld [1-%d] sec)\n", t, WATCHDOG_MAX_SEC); ++ return -EINVAL; ++ } ++ ++ /* ++ * If we are running, then reset the time value so ++ * that the new value has an immediate effect. ++ */ ++ timeout = t; ++ if (ubi32_wdt_running()) { ++ ubi32_wdt_keepalive(); ++ } ++ return 0; ++} ++ ++/* ++ * ubi32_wdt_open() ++ * Open the Device ++ */ ++static int ubi32_wdt_open(struct inode *inode, struct file *file) ++{ ++ unsigned long flags; ++ ++ if (test_and_set_bit(0, &open_check)) ++ return -EBUSY; ++ ++ if (nowayout) ++ __module_get(THIS_MODULE); ++ ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ubi32_wdt_start(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ ++ return nonseekable_open(inode, file); ++} ++ ++/* ++ * ubi32_wdt_close() ++ * Close the Device ++ */ ++static int ubi32_wdt_release(struct inode *inode, struct file *file) ++{ ++ unsigned long flags; ++ ++ /* ++ * If we don't expect a close, then the watchdog continues ++ * even though the device is closed. The caller will have ++ * a full timeout value to reopen the device and continue ++ * stroking it. ++ */ ++ if (expect_close != 42) { ++ printk(KERN_CRIT PFX ++ "Unexpected close, not stopping watchdog!\n"); ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ubi32_wdt_keepalive(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ } else { ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ubi32_wdt_stop(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ } ++ ++ expect_close = 0; ++ clear_bit(0, &open_check); ++ return 0; ++} ++ ++/* ++ * ubi32_wdt_write() ++ * Write to Device ++ * ++ * If the user writes nothing, nothing happens. ++ * If the user writes a V, then we expect a close and allow a release. ++ * If the user writes anything else, it is ignored. ++ */ ++static ssize_t ubi32_wdt_write(struct file *file, const char __user *data, ++ size_t len, loff_t *ppos) ++{ ++ size_t i; ++ unsigned long flags; ++ ++ /* ++ * Every write resets the expect_close. The last write ++ * must be a V to allow shutdown on close. ++ */ ++ expect_close = 0; ++ ++ /* ++ * Empty writes still ping. ++ */ ++ if (!len) { ++ goto ping; ++ } ++ ++ /* ++ * If nowayout is set, it does not matter if the caller ++ * is trying to send the magic 'V' we will not allow a ++ * close to stop us. ++ */ ++ if (nowayout) { ++ goto ping; ++ } ++ ++ /* ++ * See if the program wrote a 'V' and if so disable ++ * the watchdog on release. ++ */ ++ for (i = 0; i < len; i++) { ++ char c; ++ if (get_user(c, data + i)) { ++ return -EFAULT; ++ } ++ ++ if (c == 'V') { ++ expect_close = 42; ++ } ++ } ++ ++ping: ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ubi32_wdt_keepalive(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ return len; ++} ++ ++/* ++ * ubi32_wdt_ioctl() ++ * Query the watchdog device. ++ * ++ * Query basic information from the device or ping it, as outlined by the ++ * watchdog API. ++ */ ++static long ubi32_wdt_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ void __user *argp = (void __user *)arg; ++ int __user *p = argp; ++ ++ switch (cmd) { ++ case WDIOC_GETSUPPORT: ++ if (copy_to_user(argp, &ubi32_wdt_info, sizeof(ubi32_wdt_info))) { ++ return -EFAULT; ++ } ++ return 0; ++ ++ case WDIOC_GETSTATUS: { ++ unsigned long flags; ++ int running; ++ ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ running = ubi32_wdt_running(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ return running; ++ } ++ ++ case WDIOC_GETBOOTSTATUS: ++ return ubicom32_get_reset_reason(); ++ ++ case WDIOC_SETOPTIONS: { ++ unsigned long flags; ++ int options, ret = -EINVAL; ++ ++ /* ++ * The sample application does not pass a pointer ++ * but directly passes a value of 1 or 2; however ++ * all of the implementations (and thus probably ++ * the real applications) pass a pointer to a value. ++ * ++ * It should be noted that WDIOC_SETOPTIONS is defined as ++ * _IOR(WATCHDOG_IOCTL_BASE, 4, int), which means ++ * that it should be an int and NOT a pointer. ++ * ++ * TODO: Examine this code for future chips. ++ * TODO: Report the sample code defect. ++ */ ++ if ((int)p < MIN_PROCESSOR_ADDRESS) { ++ options = (int)p; ++ } else { ++ if (get_user(options, p)) ++ return -EFAULT; ++ } ++ ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ if (options & WDIOS_DISABLECARD) { ++ ubi32_wdt_stop(); ++ ret = 0; ++ } ++ if (options & WDIOS_ENABLECARD) { ++ ubi32_wdt_start(); ++ ret = 0; ++ } ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ return ret; ++ } ++ ++ case WDIOC_KEEPALIVE: { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ubi32_wdt_keepalive(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ return 0; ++ } ++ ++ case WDIOC_SETTIMEOUT: { ++ int new_timeout; ++ unsigned long flags; ++ int ret = 0; ++ ++ if (get_user(new_timeout, p)) ++ return -EFAULT; ++ ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ret = ubi32_wdt_set_timeout(new_timeout); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ return ret; ++ ++ } ++ ++ case WDIOC_GETTIMEOUT: ++ return put_user(timeout, p); ++ ++ case WDIOC_GETTIMELEFT: { ++ unsigned long flags; ++ int remaining = 0; ++ ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ remaining = ubi32_wdt_remaining(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ return put_user(remaining, p); ++ } ++ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++/* ++ * ubi32_wdt_notify_sys() ++ * Notification callback function for system events. ++ * ++ * Turn off the watchdog during a SYS_DOWN or SYS_HALT. ++ */ ++static int ubi32_wdt_notify_sys(struct notifier_block *this, ++ unsigned long code, void *unused) ++{ ++ if (code == SYS_DOWN || code == SYS_HALT) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ubi32_wdt_stop(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++#ifdef CONFIG_PM ++static int state_before_suspend; ++ ++/* ++ * ubi32_wdt_suspend() ++ * suspend the watchdog ++ * ++ * Remember if the watchdog was running and stop it. ++ */ ++static int ubi32_wdt_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ state_before_suspend = ubi32_wdt_running(); ++ ubi32_wdt_stop(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ ++ return 0; ++} ++ ++/* ++ * ubi32_wdt_resume() ++ * Resume the watchdog ++ * ++ * If the watchdog was running, turn it back on. ++ */ ++static int ubi32_wdt_resume(struct platform_device *pdev) ++{ ++ if (state_before_suspend) { ++ unsigned long flags; ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ubi32_wdt_set_timeout(timeout); ++ ubi32_wdt_start(); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ } ++ ++ return 0; ++} ++#else ++# define ubi32_wdt_suspend NULL ++# define ubi32_wdt_resume NULL ++#endif ++ ++static const struct file_operations ubi32_wdt_fops = { ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .write = ubi32_wdt_write, ++ .unlocked_ioctl = ubi32_wdt_ioctl, ++ .open = ubi32_wdt_open, ++ .release = ubi32_wdt_release, ++}; ++ ++static struct miscdevice ubi32_wdt_miscdev = { ++ .minor = WATCHDOG_MINOR, ++ .name = "watchdog", ++ .fops = &ubi32_wdt_fops, ++}; ++ ++static struct watchdog_info ubi32_wdt_info = { ++ .identity = "Ubicom32 Watchdog", ++ .options = WDIOF_SETTIMEOUT | ++ WDIOF_KEEPALIVEPING | ++ WDIOF_MAGICCLOSE, ++}; ++ ++static struct notifier_block ubi32_wdt_notifier = { ++ .notifier_call = ubi32_wdt_notify_sys, ++}; ++ ++/* ++ * ubi32_wdt_probe() ++ * Probe/register the watchdog module ++ * ++ * Registers the misc device and notifier handler. Actual device ++ * initialization is handled by ubi32_wdt_open(). ++ */ ++static int __devinit ubi32_wdt_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ ret = register_reboot_notifier(&ubi32_wdt_notifier); ++ if (ret) { ++ printk(KERN_ERR PFX ++ "cannot register reboot notifier (err=%d)\n", ret); ++ return ret; ++ } ++ ++ ret = misc_register(&ubi32_wdt_miscdev); ++ if (ret) { ++ printk(KERN_ERR PFX ++ "cannot register miscdev on minor=%d (err=%d)\n", ++ WATCHDOG_MINOR, ret); ++ unregister_reboot_notifier(&ubi32_wdt_notifier); ++ return ret; ++ } ++ ++ printk(KERN_INFO PFX "initialized: timeout=%d sec (nowayout=%d)\n", ++ timeout, nowayout); ++ ++ return 0; ++} ++ ++/* ++ * ubi32_wdt_remove() ++ * Uninstall the module ++ * ++ * Unregisters the misc device and notifier handler. Actual device ++ * deinitialization is handled by ubi32_wdt_close(). ++ */ ++static int __devexit ubi32_wdt_remove(struct platform_device *pdev) ++{ ++ misc_deregister(&ubi32_wdt_miscdev); ++ unregister_reboot_notifier(&ubi32_wdt_notifier); ++ return 0; ++} ++ ++static struct platform_device *ubi32_wdt_device; ++ ++static struct platform_driver ubi32_wdt_driver = { ++ .probe = ubi32_wdt_probe, ++ .remove = __devexit_p(ubi32_wdt_remove), ++ .suspend = ubi32_wdt_suspend, ++ .resume = ubi32_wdt_resume, ++ .driver = { ++ .name = WATCHDOG_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/* ++ * ubi32_wdt_init() ++ * Initialize the watchdog. ++ * ++ * Checks the module params and registers the platform device & driver. ++ * Real work is in the platform probe function. ++ */ ++static int __init ubi32_wdt_init(void) ++{ ++ unsigned long flags; ++ int ret; ++ ++ /* ++ * Check that the timeout value is within range ++ */ ++ spin_lock_irqsave(&ubi32_wdt_spinlock, flags); ++ ret = ubi32_wdt_set_timeout(timeout); ++ spin_unlock_irqrestore(&ubi32_wdt_spinlock, flags); ++ if (ret) { ++ return ret; ++ } ++ ++ /* ++ * Since this is an on-chip device and needs no board-specific ++ * resources, we'll handle all the platform device stuff here. ++ */ ++ ret = platform_driver_register(&ubi32_wdt_driver); ++ if (ret) { ++ printk(KERN_ERR PFX "unable to register driver\n"); ++ return ret; ++ } ++ ++ ubi32_wdt_device = platform_device_register_simple(WATCHDOG_NAME, -1, NULL, 0); ++ if (IS_ERR(ubi32_wdt_device)) { ++ printk(KERN_ERR PFX "unable to register device\n"); ++ platform_driver_unregister(&ubi32_wdt_driver); ++ return PTR_ERR(ubi32_wdt_device); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ubi32_wdt_exit() ++ * Deinitialize module ++ * ++ * Back out the platform device & driver steps. Real work is in the ++ * platform remove function. ++ */ ++static void __exit ubi32_wdt_exit(void) ++{ ++ platform_device_unregister(ubi32_wdt_device); ++ platform_driver_unregister(&ubi32_wdt_driver); ++} ++ ++module_init(ubi32_wdt_init); ++module_exit(ubi32_wdt_exit); ++ ++MODULE_AUTHOR("Sol Kavy<sol@ubicom.com>"); ++MODULE_DESCRIPTION("Ubicom32 Watchdog Device Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); ++ ++module_param(timeout, uint, 0); ++MODULE_PARM_DESC(timeout, ++ "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default=" ++ __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); ++ ++module_param(nowayout, int, 0); ++MODULE_PARM_DESC(nowayout, ++ "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +--- a/fs/binfmt_flat.c ++++ b/fs/binfmt_flat.c +@@ -54,6 +54,10 @@ + #define DBG_FLT(a...) + #endif + ++#ifndef ARCH_FLAT_ALIGN ++#define ARCH_FLAT_ALIGN 0 ++#endif ++ + #define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */ + #define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */ + +@@ -127,10 +131,10 @@ static unsigned long create_flat_tables( + --sp; put_user((unsigned long) argv, sp); + } + +- put_user(argc,--sp); ++ --sp; put_user(argc,sp); + current->mm->arg_start = (unsigned long) p; + while (argc-->0) { +- put_user((unsigned long) p, argv++); ++ put_user((unsigned long) p, argv); argv++; + do { + get_user(dummy, p); p++; + } while (dummy); +@@ -425,6 +429,7 @@ static int load_flat_file(struct linux_b + loff_t fpos; + unsigned long start_code, end_code; + int ret; ++ int flush_happened = 0; + + hdr = ((struct flat_hdr *) bprm->buf); /* exec-header */ + inode = bprm->file->f_path.dentry->d_inode; +@@ -510,6 +515,7 @@ static int load_flat_file(struct linux_b + + /* OK, This is the point of no return */ + set_personality(PER_LINUX_32BIT); ++ flush_happened = 1; + } + + /* +@@ -524,6 +530,12 @@ static int load_flat_file(struct linux_b + * it all together. + */ + if ((flags & (FLAT_FLAG_RAM|FLAT_FLAG_GZIP)) == 0) { ++ ++#ifdef ARCH_FLAT_ALIGN_TEXT ++ printk("Unable to mmap rom with ARCH alignment requirements\n"); ++ ret = -ENOEXEC; ++ goto err; ++#endif + /* + * this should give us a ROM ptr, but if it doesn't we don't + * really care +@@ -542,7 +554,7 @@ static int load_flat_file(struct linux_b + goto err; + } + +- len = data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long); ++ len = data_len + extra + ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN); + down_write(¤t->mm->mmap_sem); + realdatastart = do_mmap(0, 0, len, + PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 0); +@@ -565,7 +577,7 @@ static int load_flat_file(struct linux_b + ret = realdatastart; + goto err; + } +- datapos = realdatastart + MAX_SHARED_LIBS * sizeof(unsigned long); ++ datapos = realdatastart + ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN); + + DBG_FLT("BINFMT_FLAT: Allocated data+bss+stack (%d bytes): %x\n", + (int)(data_len + bss_len + stack_len), (int)datapos); +@@ -594,7 +606,11 @@ static int load_flat_file(struct linux_b + + } else { + +- len = text_len + data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long); ++ len = text_len + data_len + extra + ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN); ++#ifdef ARCH_FLAT_ALIGN_TEXT ++ /* Reserve space for the text alignment. */ ++ len += ARCH_FLAT_ALIGN; ++#endif + down_write(¤t->mm->mmap_sem); + textpos = do_mmap(0, 0, len, + PROT_READ | PROT_EXEC | PROT_WRITE, MAP_PRIVATE, 0); +@@ -617,11 +633,14 @@ static int load_flat_file(struct linux_b + goto err; + } + ++ memp = textpos; ++#ifdef ARCH_FLAT_ALIGN_TEXT ++ textpos = ALIGN(textpos + sizeof(struct flat_hdr), ARCH_FLAT_ALIGN) - sizeof(struct flat_hdr); ++#endif + realdatastart = textpos + ntohl(hdr->data_start); +- datapos = realdatastart + MAX_SHARED_LIBS * sizeof(unsigned long); ++ datapos = realdatastart + ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN); + reloc = (unsigned long *) (textpos + ntohl(hdr->reloc_start) + +- MAX_SHARED_LIBS * sizeof(unsigned long)); +- memp = textpos; ++ ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN)); + + #ifdef CONFIG_BINFMT_ZFLAT + /* +@@ -657,7 +676,7 @@ static int load_flat_file(struct linux_b + } + if (result >= (unsigned long)-4096) { + printk("Unable to read code+data+bss, errno %d\n",(int)-result); +- do_munmap(current->mm, textpos, text_len + data_len + extra + ++ do_munmap(current->mm, memp, text_len + data_len + extra + + MAX_SHARED_LIBS * sizeof(unsigned long)); + ret = result; + goto err; +@@ -670,6 +689,9 @@ static int load_flat_file(struct linux_b + + /* The main program needs a little extra setup in the task structure */ + start_code = textpos + sizeof (struct flat_hdr); ++#ifdef ARCH_FLAT_ALIGN_TEXT ++ BUG_ON(ALIGN(start_code, ARCH_FLAT_ALIGN) != start_code); ++#endif + end_code = textpos + text_len; + if (id == 0) { + current->mm->start_code = start_code; +@@ -796,6 +818,13 @@ static int load_flat_file(struct linux_b + + return 0; + err: ++ if (flush_happened) { ++ /* ++ * The parent process has already started running. We cannot allow the child to return back to user space ++ * as this child is still uning the parent stack and 2 will clobber each other. We are going to kill this child. ++ */ ++ do_exit(SIGTERM); ++ } + return ret; + } + +--- a/fs/Kconfig.binfmt ++++ b/fs/Kconfig.binfmt +@@ -30,7 +30,7 @@ config COMPAT_BINFMT_ELF + config BINFMT_ELF_FDPIC + bool "Kernel support for FDPIC ELF binaries" + default y +- depends on (FRV || BLACKFIN || (SUPERH32 && !MMU)) ++ depends on (FRV || BLACKFIN || (SUPERH32 && !MMU) || UBICOM32) + help + ELF FDPIC binaries are based on ELF, but allow the individual load + segments of a binary to be located in memory independently of each +--- a/include/asm-generic/dma-mapping.h ++++ b/include/asm-generic/dma-mapping.h +@@ -255,7 +255,7 @@ dma_sync_sg_for_device(struct device *de + } + + static inline int +-dma_error(dma_addr_t dma_addr) ++dma_mapping_error(struct device *dev, dma_addr_t dma_addr) + { + return 0; + } +--- a/include/linux/elf-em.h ++++ b/include/linux/elf-em.h +@@ -41,6 +41,7 @@ + * up with a final number. + */ + #define EM_ALPHA 0x9026 ++#define EM_UBICOM32 0xde3d /* Ubicom32; no ABI */ + + /* Bogus old v850 magic number, used by old tools. */ + #define EM_CYGNUS_V850 0x9080 +--- a/include/linux/oprofile.h ++++ b/include/linux/oprofile.h +@@ -100,6 +100,8 @@ void oprofile_add_sample(struct pt_regs + */ + void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, + unsigned long event, int is_kernel); ++void oprofile_add_ext_sample_cpu(unsigned long pc, struct pt_regs * const regs, ++ unsigned long event, int is_kernel, int cpu); + + /* Use this instead when the PC value is not from the regs. Doesn't + * backtrace. */ +--- a/include/linux/serial_core.h ++++ b/include/linux/serial_core.h +@@ -158,6 +158,9 @@ + /* SH-SCI */ + #define PORT_SCIFA 83 + ++/* Ubicom32 */ ++#define PORT_UBI32_UARTTIO 84 ++ + #ifdef __KERNEL__ + + #include <linux/compiler.h> +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -2624,6 +2624,9 @@ static int m_show(struct seq_file *m, vo + /* Used by oprofile and other similar tools. */ + seq_printf(m, " 0x%p", mod->module_core); + ++#ifdef ARCH_PROC_MODULES_EXTRA ++ ARCH_PROC_MODULES_EXTRA(m, mod); ++#endif + /* Taints info */ + if (mod->taints) + seq_printf(m, " %s", module_flags(mod, buf)); +--- a/kernel/sched_clock.c ++++ b/kernel/sched_clock.c +@@ -37,7 +37,7 @@ + */ + unsigned long long __attribute__((weak)) sched_clock(void) + { +- return (unsigned long long)jiffies * (NSEC_PER_SEC / HZ); ++ return (get_jiffies_64() - INITIAL_JIFFIES) * (NSEC_PER_SEC / HZ); + } + + static __read_mostly int sched_clock_running; +--- a/lib/Kconfig.debug ++++ b/lib/Kconfig.debug +@@ -549,7 +549,7 @@ config FRAME_POINTER + bool "Compile the kernel with frame pointers" + depends on DEBUG_KERNEL && \ + (X86 || CRIS || M68K || M68KNOMMU || FRV || UML || S390 || \ +- AVR32 || SUPERH || BLACKFIN || MN10300) ++ AVR32 || SUPERH || BLACKFIN || MN10300 || UBICOM32) + default y if DEBUG_INFO && UML + help + If you say Y here the resulting kernel image will be slightly larger +--- a/mm/Makefile ++++ b/mm/Makefile +@@ -34,3 +34,5 @@ obj-$(CONFIG_MIGRATION) += migrate.o + obj-$(CONFIG_SMP) += allocpercpu.o + obj-$(CONFIG_QUICKLIST) += quicklist.o + obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o ++ ++CFLAGS_slab.o := $(PROFILING) -O2 +--- a/scripts/mod/file2alias.c ++++ b/scripts/mod/file2alias.c +@@ -753,6 +753,15 @@ void handle_moddevtable(struct module *m + + sym->st_value; + } + ++ /* ++ * somehow our gcc is not generating st_size correctly and set 0 for some symbols. ++ * and 0 size will break do_table since it adjust size to (size - id_size) ++ * this is to make sure st_size fall in range. ++ */ ++ if (sym->st_size == 0 || sym->st_size > info->sechdrs[sym->st_shndx].sh_size) { ++ sym->st_size = info->sechdrs[sym->st_shndx].sh_size; ++ } ++ + if (sym_is(symname, "__mod_pci_device_table")) + do_table(symval, sym->st_size, + sizeof(struct pci_device_id), "pci", +--- a/sound/Kconfig ++++ b/sound/Kconfig +@@ -80,6 +80,8 @@ source "sound/parisc/Kconfig" + + source "sound/soc/Kconfig" + ++source "sound/ubicom32/Kconfig" ++ + endif # SND + + menuconfig SOUND_PRIME +--- a/sound/Makefile ++++ b/sound/Makefile +@@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += sound_firmw + obj-$(CONFIG_SOUND_PRIME) += oss/ + obj-$(CONFIG_DMASOUND) += oss/ + obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ +- sparc/ spi/ parisc/ pcmcia/ mips/ soc/ ++ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ ubicom32/ + obj-$(CONFIG_SND_AOA) += aoa/ + + # This one must be compilable even if sound is configured out +--- /dev/null ++++ b/sound/ubicom32/Kconfig +@@ -0,0 +1,36 @@ ++# ALSA Ubicom32 drivers ++ ++menuconfig SND_UBI32 ++ tristate "Ubicom32 sound devices" ++ select SND_PCM ++ default n ++ help ++ Say Y here to include support for audio on the Ubicom32 platform. ++ To compile this driver as a module, say M here: the module will be ++ called snd_ubi32. ++ ++if SND_UBI32 ++ ++config SND_UBI32_AUDIO_GENERIC ++ bool "Generic Codec Support" ++ default n ++ help ++ Generic codecs ++ ++comment "I2C Based Codecs" ++ ++config SND_UBI32_AUDIO_CS4350 ++ bool "Cirrus Logic CS4350 DAC" ++ depends on I2C ++ default n ++ help ++ Support for the Cirrus Logic CS4350 DAC. ++ ++config SND_UBI32_AUDIO_CS4384 ++ bool "Cirrus Logic CS4384 DAC" ++ depends on I2C ++ default n ++ help ++ Support for the Cirrus Logic CS4384 DAC. ++ ++endif #SND_UBI32 +--- /dev/null ++++ b/sound/ubicom32/Makefile +@@ -0,0 +1,39 @@ ++# ++# sound/ubicom32/Makefile ++# Makefile for ALSA ++# ++# (C) Copyright 2009, Ubicom, Inc. ++# ++# This file is part of the Ubicom32 Linux Kernel Port. ++# ++# The Ubicom32 Linux Kernel Port is free software: you can redistribute ++# it and/or modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation, either version 2 of the ++# License, or (at your option) any later version. ++# ++# The Ubicom32 Linux Kernel Port is distributed in the hope that it ++# will be useful, but WITHOUT ANY WARRANTY; without even the implied ++# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with the Ubicom32 Linux Kernel Port. If not, ++# see <http://www.gnu.org/licenses/>. ++# ++# Ubicom32 implementation derived from (with many thanks): ++# arch/m68knommu ++# arch/blackfin ++# arch/parisc ++# ++ ++CFLAGS_ubi32.o += -O2 ++snd-ubi32-pcm-objs := ubi32-pcm.o ++snd-ubi32-generic-objs := ubi32-generic.o ++snd-ubi32-cs4350-objs := ubi32-cs4350.o ++snd-ubi32-cs4384-objs := ubi32-cs4384.o ++ ++# Toplevel Module Dependency ++obj-$(CONFIG_SND_UBI32) += snd-ubi32-pcm.o ++obj-$(CONFIG_SND_UBI32_AUDIO_GENERIC) += snd-ubi32-generic.o ++obj-$(CONFIG_SND_UBI32_AUDIO_CS4350) += snd-ubi32-cs4350.o ++obj-$(CONFIG_SND_UBI32_AUDIO_CS4384) += snd-ubi32-cs4384.o +--- /dev/null ++++ b/sound/ubicom32/ubi32-cs4350.c +@@ -0,0 +1,286 @@ ++/* ++ * sound/ubicom32/ubi32-cs4350.c ++ * Interface to ubicom32 virtual audio peripheral - using CS4350 DAC ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/i2c.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/initval.h> ++#include "ubi32.h" ++ ++#define DRIVER_NAME "snd-ubi32-cs4350" ++ ++/* ++ * Module properties ++ */ ++static const struct i2c_device_id snd_ubi32_cs4350_id[] = { ++ {"cs4350", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ubicom32audio_id); ++ ++static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ ++ ++/* ++ * snd_ubi32_cs4350_free ++ * Card private data free function ++ */ ++void snd_ubi32_cs4350_free(struct snd_card *card) ++{ ++ //struct ubi32_snd_priv *ubi32_priv = card->private_data; ++ ++ /* ++ * Free all the fields in the snd_ubi32_priv struct ++ */ ++ // Nothing to free at this time because ubi32_priv just maintains pointers ++} ++ ++/* ++ * snd_ubi32_cs4350_dac_init ++ */ ++static int snd_ubi32_cs4350_dac_init(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ int ret; ++ char send[2]; ++ char recv[8]; ++ ++ /* ++ * Initialize the CS4350 DAC over the I2C interface ++ */ ++ snd_printk(KERN_INFO "Initializing CS4350 DAC\n"); ++ ++ /* ++ * Register 0x01: device/revid ++ */ ++ send[0] = 0x01; ++ ret = i2c_master_send(client, send, 1); ++ if (ret != 1) { ++ snd_printk(KERN_ERR "Failed 1st attempt to write to CS4350 register 0x01\n"); ++ goto fail; ++ } ++ ret = i2c_master_recv(client, recv, 1); ++ if (ret != 1) { ++ snd_printk(KERN_ERR "Failed initial read of CS4350 registers\n"); ++ goto fail; ++ } ++ snd_printk(KERN_INFO "CS4350 DAC Device/Rev: %08x\n", recv[0]); ++ ++ /* ++ * Register 0x02: Mode control ++ * I2S DIF[2:0] = 001, no De-Emphasis, Auto speed mode ++ */ ++ send[0] = 0x02; ++ send[1] = 0x10; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to set CS4350 to I2S mode\n"); ++ goto fail; ++ } ++ ++ /* ++ * Register 0x05/0x06: Volume control ++ * Channel A volume set to -16 dB ++ * Channel B volume set to -16 dB ++ */ ++ send[0] = 0x05; ++ send[1] = 0x00; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to set channel A volume on CS4350\n"); ++ goto fail; ++ } ++ ++ send[0] = 0x06; ++ send[1] = 0x00; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to set channel A volume on CS4350\n"); ++ goto fail; ++ } ++ ++ /* ++ * Make sure the changes took place, this helps verify we are talking to ++ * the correct chip. ++ */ ++ send[0] = 0x81; ++ ret = i2c_master_send(client, send, 1); ++ if (ret != 1) { ++ snd_printk(KERN_ERR "Failed to initiate readback\n"); ++ goto fail; ++ } ++ ++ ret = i2c_master_recv(client, recv, 8); ++ if (ret != 8) { ++ snd_printk(KERN_ERR "Failed second read of CS4350 registers\n"); ++ goto fail; ++ } ++ ++ if ((recv[1] != 0x10) || (recv[4] != 0x00) || (recv[5] != 0x00)) { ++ snd_printk(KERN_ERR "Failed to initialize CS4350 DAC\n"); ++ goto fail; ++ } ++ ++ snd_printk(KERN_INFO "CS4350 DAC Initialized\n"); ++ return 0; ++ ++fail: ++ return -ENODEV; ++} ++ ++/* ++ * snd_ubi32_cs4350_i2c_probe ++ */ ++static int snd_ubi32_cs4350_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ struct snd_card *card; ++ struct ubi32_snd_priv *ubi32_priv; ++ int err, ret; ++ struct platform_device *pdev; ++ ++ pdev = client->dev.platform_data; ++ if (!pdev) { ++ return -ENODEV; ++ } ++ ++ /* ++ * Initialize the CS4350 DAC ++ */ ++ ret = snd_ubi32_cs4350_dac_init(client, id); ++ if (ret < 0) { ++ /* ++ * Initialization failed. Propagate the error. ++ */ ++ return ret; ++ } ++ ++ /* ++ * Create a snd_card structure ++ */ ++ card = snd_card_new(index, "Ubi32-CS4350", THIS_MODULE, sizeof(struct ubi32_snd_priv)); ++ if (card == NULL) { ++ return -ENOMEM; ++ } ++ ++ card->private_free = snd_ubi32_cs4350_free; /* Not sure if correct */ ++ ubi32_priv = card->private_data; ++ ++ /* ++ * CS4350 DAC has a minimum sample rate of 30khz and an ++ * upper limit of 216khz for it's auto-detect. ++ */ ++ ubi32_priv->min_sample_rate = 30000; ++ ubi32_priv->max_sample_rate = 216000; ++ ++ /* ++ * Initialize the snd_card's private data structure ++ */ ++ ubi32_priv->card = card; ++ ++ /* ++ * Create the new PCM instance ++ */ ++ err = snd_ubi32_pcm_probe(ubi32_priv, pdev); ++ if (err < 0) { ++ snd_card_free(card); ++ return err; /* What is err? Need to include correct file */ ++ } ++ ++ strcpy(card->driver, "Ubi32-CS4350"); ++ strcpy(card->shortname, "Ubi32-CS4350"); ++ sprintf(card->longname, "%s at sendirq=%d recvirq=%d", card->shortname, ubi32_priv->tx_irq, ubi32_priv->rx_irq); ++ ++ snd_card_set_dev(card, &client->dev); ++ ++ /* ++ * Register the sound card ++ */ ++ if ((err = snd_card_register(card)) != 0) { ++ snd_printk(KERN_INFO "snd_card_register error\n"); ++ } ++ ++ /* ++ * Store card for access from other methods ++ */ ++ i2c_set_clientdata(client, card); ++ ++ return 0; ++} ++ ++/* ++ * snd_ubi32_cs4350_i2c_remove ++ */ ++static int __devexit snd_ubi32_cs4350_i2c_remove(struct i2c_client *client) ++{ ++ struct snd_card *card; ++ struct ubi32_snd_priv *ubi32_priv; ++ ++ card = i2c_get_clientdata(client); ++ ++ ubi32_priv = card->private_data; ++ snd_ubi32_pcm_remove(ubi32_priv); ++ ++ snd_card_free(i2c_get_clientdata(client)); ++ i2c_set_clientdata(client, NULL); ++ ++ return 0; ++} ++ ++/* ++ * I2C driver description ++ */ ++static struct i2c_driver snd_ubi32_cs4350_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .id_table = snd_ubi32_cs4350_id, ++ .probe = snd_ubi32_cs4350_i2c_probe, ++ .remove = __devexit_p(snd_ubi32_cs4350_i2c_remove), ++}; ++ ++/* ++ * Driver init ++ */ ++static int __init snd_ubi32_cs4350_init(void) ++{ ++ return i2c_add_driver(&snd_ubi32_cs4350_driver); ++} ++module_init(snd_ubi32_cs4350_init); ++ ++/* ++ * snd_ubi32_cs4350_exit ++ */ ++static void __exit snd_ubi32_cs4350_exit(void) ++{ ++ i2c_del_driver(&snd_ubi32_cs4350_driver); ++} ++module_exit(snd_ubi32_cs4350_exit); ++ ++/* ++ * Module properties ++ */ ++MODULE_ALIAS("i2c:" DRIVER_NAME); ++MODULE_AUTHOR("Patrick Tjin"); ++MODULE_DESCRIPTION("Driver for Ubicom32 audio devices CS4350"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/sound/ubicom32/ubi32-cs4384.c +@@ -0,0 +1,982 @@ ++/* ++ * sound/ubicom32/ubi32-cs4384.c ++ * Interface to ubicom32 virtual audio peripheral - using CS4384 DAC ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/i2c.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/delay.h> ++#include <sound/core.h> ++#include <sound/tlv.h> ++#include <sound/control.h> ++#include <sound/pcm.h> ++#include <sound/initval.h> ++#include <asm/ip5000.h> ++#include <asm/gpio.h> ++#include <asm/audio_tio.h> ++#include <asm/ubi32-cs4384.h> ++#include "ubi32.h" ++ ++#define DRIVER_NAME "snd-ubi32-cs4384" ++ ++/* ++ * Module properties ++ */ ++static const struct i2c_device_id snd_ubi32_cs4384_id[] = { ++ {"cs4384", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ubicom32audio_id); ++ ++static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ ++ ++/* ++ * Mixer properties ++ */ ++enum { ++ /* ++ * Be careful of changing the order of these IDs, they ++ * are used to index the volume array. ++ */ ++ SND_UBI32_CS4384_FRONT_ID, ++ SND_UBI32_CS4384_SURROUND_ID, ++ SND_UBI32_CS4384_CENTER_ID, ++ SND_UBI32_CS4384_LFE_ID, ++ SND_UBI32_CS4384_REAR_ID, ++ ++ /* ++ * This should be the last ID ++ */ ++ SND_UBI32_CS4384_LAST_ID, ++}; ++static const u8_t snd_ubi32_cs4384_ch_ofs[] = {0, 2, 4, 5, 6}; ++ ++static const DECLARE_TLV_DB_SCALE(snd_ubi32_cs4384_db, -12750, 50, 0); ++ ++#define snd_ubi32_cs4384_info_mute snd_ctl_boolean_stereo_info ++#define snd_ubi32_cs4384_info_mute_mono snd_ctl_boolean_mono_info ++ ++/* ++ * Mixer controls ++ */ ++static int snd_ubi32_cs4384_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); ++static int snd_ubi32_cs4384_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); ++static int snd_ubi32_cs4384_put_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); ++static int snd_ubi32_cs4384_get_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); ++static int snd_ubi32_cs4384_put_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); ++ ++/* ++ * Make sure to update these if the structure below is changed ++ */ ++#define SND_UBI32_MUTE_CTL_START 5 ++#define SND_UBI32_MUTE_CTL_END 9 ++static struct snd_kcontrol_new snd_ubi32_cs4384_controls[] __devinitdata = { ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Front Playback Volume", ++ .info = snd_ubi32_cs4384_info_volume, ++ .get = snd_ubi32_cs4384_get_volume, ++ .put = snd_ubi32_cs4384_put_volume, ++ .private_value = SND_UBI32_CS4384_FRONT_ID, ++ .tlv = { ++ .p = snd_ubi32_cs4384_db, ++ }, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Surround Playback Volume", ++ .info = snd_ubi32_cs4384_info_volume, ++ .get = snd_ubi32_cs4384_get_volume, ++ .put = snd_ubi32_cs4384_put_volume, ++ .private_value = SND_UBI32_CS4384_SURROUND_ID, ++ .tlv = { ++ .p = snd_ubi32_cs4384_db, ++ }, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Center Playback Volume", ++ .info = snd_ubi32_cs4384_info_volume, ++ .get = snd_ubi32_cs4384_get_volume, ++ .put = snd_ubi32_cs4384_put_volume, ++ .private_value = SND_UBI32_CS4384_CENTER_ID, ++ .tlv = { ++ .p = snd_ubi32_cs4384_db, ++ }, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "LFE Playback Volume", ++ .info = snd_ubi32_cs4384_info_volume, ++ .get = snd_ubi32_cs4384_get_volume, ++ .put = snd_ubi32_cs4384_put_volume, ++ .private_value = SND_UBI32_CS4384_LFE_ID, ++ .tlv = { ++ .p = snd_ubi32_cs4384_db, ++ }, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Rear Playback Volume", ++ .info = snd_ubi32_cs4384_info_volume, ++ .get = snd_ubi32_cs4384_get_volume, ++ .put = snd_ubi32_cs4384_put_volume, ++ .private_value = SND_UBI32_CS4384_REAR_ID, ++ .tlv = { ++ .p = snd_ubi32_cs4384_db, ++ }, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Front Playback Switch", ++ .info = snd_ubi32_cs4384_info_mute, ++ .get = snd_ubi32_cs4384_get_mute, ++ .put = snd_ubi32_cs4384_put_mute, ++ .private_value = SND_UBI32_CS4384_FRONT_ID, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Surround Playback Switch", ++ .info = snd_ubi32_cs4384_info_mute, ++ .get = snd_ubi32_cs4384_get_mute, ++ .put = snd_ubi32_cs4384_put_mute, ++ .private_value = SND_UBI32_CS4384_SURROUND_ID, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Center Playback Switch", ++ .info = snd_ubi32_cs4384_info_mute_mono, ++ .get = snd_ubi32_cs4384_get_mute, ++ .put = snd_ubi32_cs4384_put_mute, ++ .private_value = SND_UBI32_CS4384_CENTER_ID, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "LFE Playback Switch", ++ .info = snd_ubi32_cs4384_info_mute_mono, ++ .get = snd_ubi32_cs4384_get_mute, ++ .put = snd_ubi32_cs4384_put_mute, ++ .private_value = SND_UBI32_CS4384_LFE_ID, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Rear Playback Switch", ++ .info = snd_ubi32_cs4384_info_mute, ++ .get = snd_ubi32_cs4384_get_mute, ++ .put = snd_ubi32_cs4384_put_mute, ++ .private_value = SND_UBI32_CS4384_REAR_ID, ++ }, ++}; ++ ++/* ++ * Our private data ++ */ ++struct snd_ubi32_cs4384_priv { ++ /* ++ * Array of current volumes ++ * (L, R, SL, SR, C, LFE, RL, RR) ++ */ ++ uint8_t volume[8]; ++ ++ /* ++ * Bitmask of mutes ++ * MSB (RR, RL, LFE, C, SR, SL, R, L) LSB ++ */ ++ uint8_t mute; ++ ++ /* ++ * Array of controls ++ */ ++ struct snd_kcontrol *kctls[ARRAY_SIZE(snd_ubi32_cs4384_controls)]; ++ ++ /* ++ * Lock to protect our card ++ */ ++ spinlock_t lock; ++}; ++ ++/* ++ * snd_ubi32_cs4384_info_volume ++ */ ++static int snd_ubi32_cs4384_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) ++{ ++ unsigned int id = (unsigned int)kcontrol->private_value; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ if ((id != SND_UBI32_CS4384_LFE_ID) && ++ (id != SND_UBI32_CS4384_CENTER_ID)) { ++ uinfo->count = 2; ++ } ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 255; ++ return 0; ++} ++ ++/* ++ * snd_ubi32_cs4384_get_volume ++ */ ++static int snd_ubi32_cs4384_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ubi32_snd_priv *priv = snd_kcontrol_chip(kcontrol); ++ struct snd_ubi32_cs4384_priv *cs4384_priv; ++ unsigned int id = (unsigned int)kcontrol->private_value; ++ int ch = snd_ubi32_cs4384_ch_ofs[id]; ++ unsigned long flags; ++ ++ if (id >= SND_UBI32_CS4384_LAST_ID) { ++ return -EINVAL; ++ } ++ ++ cs4384_priv = snd_ubi32_priv_get_drv(priv); ++ ++ spin_lock_irqsave(&cs4384_priv->lock, flags); ++ ++ ucontrol->value.integer.value[0] = cs4384_priv->volume[ch]; ++ if ((id != SND_UBI32_CS4384_LFE_ID) && ++ (id != SND_UBI32_CS4384_CENTER_ID)) { ++ ch++; ++ ucontrol->value.integer.value[1] = cs4384_priv->volume[ch]; ++ } ++ ++ spin_unlock_irqrestore(&cs4384_priv->lock, flags); ++ ++ return 0; ++} ++ ++/* ++ * snd_ubi32_cs4384_put_volume ++ */ ++static int snd_ubi32_cs4384_put_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ubi32_snd_priv *priv = snd_kcontrol_chip(kcontrol); ++ struct i2c_client *client = (struct i2c_client *)priv->client; ++ struct snd_ubi32_cs4384_priv *cs4384_priv; ++ unsigned int id = (unsigned int)kcontrol->private_value; ++ int ch = snd_ubi32_cs4384_ch_ofs[id]; ++ unsigned long flags; ++ unsigned char send[3]; ++ int nch; ++ int ret = -EINVAL; ++ ++ if (id >= SND_UBI32_CS4384_LAST_ID) { ++ return -EINVAL; ++ } ++ ++ cs4384_priv = snd_ubi32_priv_get_drv(priv); ++ ++ spin_lock_irqsave(&cs4384_priv->lock, flags); ++ ++ send[0] = 0; ++ switch (id) { ++ case SND_UBI32_CS4384_REAR_ID: ++ send[0] = 0x06; ++ ++ /* ++ * Fall through ++ */ ++ ++ case SND_UBI32_CS4384_SURROUND_ID: ++ send[0] += 0x03; ++ ++ /* ++ * Fall through ++ */ ++ ++ case SND_UBI32_CS4384_FRONT_ID: ++ send[0] += 0x8B; ++ nch = 2; ++ send[1] = 255 - (ucontrol->value.integer.value[0] & 0xFF); ++ send[2] = 255 - (ucontrol->value.integer.value[1] & 0xFF); ++ cs4384_priv->volume[ch++] = send[1]; ++ cs4384_priv->volume[ch] = send[2]; ++ break; ++ ++ case SND_UBI32_CS4384_LFE_ID: ++ send[0] = 0x81; ++ ++ /* ++ * Fall through ++ */ ++ ++ case SND_UBI32_CS4384_CENTER_ID: ++ send[0] += 0x11; ++ nch = 1; ++ send[1] = 255 - (ucontrol->value.integer.value[0] & 0xFF); ++ cs4384_priv->volume[ch] = send[1]; ++ break; ++ ++ default: ++ spin_unlock_irqrestore(&cs4384_priv->lock, flags); ++ goto done; ++ ++ } ++ ++ /* ++ * Send the volume to the chip ++ */ ++ nch++; ++ ret = i2c_master_send(client, send, nch); ++ if (ret != nch) { ++ snd_printk(KERN_ERR "Failed to set volume on CS4384\n"); ++ } ++ ++done: ++ spin_unlock_irqrestore(&cs4384_priv->lock, flags); ++ ++ return ret; ++} ++ ++/* ++ * snd_ubi32_cs4384_get_mute ++ */ ++static int snd_ubi32_cs4384_get_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ubi32_snd_priv *priv = snd_kcontrol_chip(kcontrol); ++ struct snd_ubi32_cs4384_priv *cs4384_priv; ++ unsigned int id = (unsigned int)kcontrol->private_value; ++ int ch = snd_ubi32_cs4384_ch_ofs[id]; ++ unsigned long flags; ++ ++ if (id >= SND_UBI32_CS4384_LAST_ID) { ++ return -EINVAL; ++ } ++ ++ cs4384_priv = snd_ubi32_priv_get_drv(priv); ++ ++ spin_lock_irqsave(&cs4384_priv->lock, flags); ++ ++ ucontrol->value.integer.value[0] = !(cs4384_priv->mute & (1 << ch)); ++ ++ if ((id != SND_UBI32_CS4384_LFE_ID) && ++ (id != SND_UBI32_CS4384_CENTER_ID)) { ++ ch++; ++ ucontrol->value.integer.value[1] = !(cs4384_priv->mute & (1 << ch)); ++ } ++ ++ spin_unlock_irqrestore(&cs4384_priv->lock, flags); ++ ++ return 0; ++} ++ ++/* ++ * snd_ubi32_cs4384_put_mute ++ */ ++static int snd_ubi32_cs4384_put_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct ubi32_snd_priv *priv = snd_kcontrol_chip(kcontrol); ++ struct i2c_client *client = (struct i2c_client *)priv->client; ++ struct snd_ubi32_cs4384_priv *cs4384_priv; ++ unsigned int id = (unsigned int)kcontrol->private_value; ++ int ch = snd_ubi32_cs4384_ch_ofs[id]; ++ unsigned long flags; ++ unsigned char send[2]; ++ int ret = -EINVAL; ++ ++ if (id >= SND_UBI32_CS4384_LAST_ID) { ++ return -EINVAL; ++ } ++ ++ cs4384_priv = snd_ubi32_priv_get_drv(priv); ++ ++ spin_lock_irqsave(&cs4384_priv->lock, flags); ++ ++ if (ucontrol->value.integer.value[0]) { ++ cs4384_priv->mute &= ~(1 << ch); ++ } else { ++ cs4384_priv->mute |= (1 << ch); ++ } ++ ++ if ((id != SND_UBI32_CS4384_LFE_ID) && (id != SND_UBI32_CS4384_CENTER_ID)) { ++ ch++; ++ if (ucontrol->value.integer.value[1]) { ++ cs4384_priv->mute &= ~(1 << ch); ++ } else { ++ cs4384_priv->mute |= (1 << ch); ++ } ++ } ++ ++ /* ++ * Update the chip's mute reigster ++ */ ++ send[0] = 0x09; ++ send[1] = cs4384_priv->mute; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to set mute on CS4384\n"); ++ } ++ ++ spin_unlock_irqrestore(&cs4384_priv->lock, flags); ++ ++ return ret; ++} ++ ++/* ++ * snd_ubi32_cs4384_mixer ++ * Setup the mixer controls ++ */ ++static int __devinit snd_ubi32_cs4384_mixer(struct ubi32_snd_priv *priv) ++{ ++ struct snd_card *card = priv->card; ++ struct snd_ubi32_cs4384_priv *cs4384_priv; ++ int i; ++ ++ cs4384_priv = snd_ubi32_priv_get_drv(priv); ++ for (i = 0; i < ARRAY_SIZE(snd_ubi32_cs4384_controls); i++) { ++ int err; ++ ++ cs4384_priv->kctls[i] = snd_ctl_new1(&snd_ubi32_cs4384_controls[i], priv); ++ err = snd_ctl_add(card, cs4384_priv->kctls[i]); ++ if (err) { ++ snd_printk(KERN_WARNING "Failed to add control %d\n", i); ++ return err; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * snd_ubi32_cs4384_free ++ * Card private data free function ++ */ ++void snd_ubi32_cs4384_free(struct snd_card *card) ++{ ++ struct snd_ubi32_cs4384_priv *cs4384_priv; ++ struct ubi32_snd_priv *ubi32_priv; ++ ++ ubi32_priv = card->private_data; ++ cs4384_priv = snd_ubi32_priv_get_drv(ubi32_priv); ++ if (cs4384_priv) { ++ kfree(cs4384_priv); ++ } ++} ++ ++/* ++ * snd_ubi32_cs4384_setup_mclk ++ */ ++static int snd_ubi32_cs4384_setup_mclk(struct ubi32_cs4384_platform_data *pdata) ++{ ++ struct ubicom32_io_port *ioa = (struct ubicom32_io_port *)RA; ++ struct ubicom32_io_port *ioc = (struct ubicom32_io_port *)RC; ++ struct ubicom32_io_port *ioh = (struct ubicom32_io_port *)RH; ++ unsigned int ctl0; ++ unsigned int ctlx; ++ unsigned int div; ++ ++ div = pdata->mclk_entries[0].div; ++ ++ ctl0 = (1 << 13); ++ ctlx = ((div - 1) << 16) | (div / 2); ++ ++ switch (pdata->mclk_src) { ++ case UBI32_CS4384_MCLK_PWM_0: ++ ioc->function |= 2; ++ ioc->ctl0 |= ctl0; ++ ioc->ctl1 = ctlx; ++ return 0; ++ ++ case UBI32_CS4384_MCLK_PWM_1: ++ ioc->function |= 2; ++ ioc->ctl0 |= ctl0 << 16; ++ ioc->ctl2 = ctlx; ++ return 0; ++ ++ case UBI32_CS4384_MCLK_PWM_2: ++ ioh->ctl0 |= ctl0; ++ ioh->ctl1 = ctlx; ++ return 0; ++ ++ case UBI32_CS4384_MCLK_CLKDIV_1: ++ ioa->gpio_mask &= (1 << 7); ++ ioa->ctl1 &= ~(0x7F << 14); ++ ioa->ctl1 |= ((div - 1) << 14); ++ return 0; ++ ++ case UBI32_CS4384_MCLK_OTHER: ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/* ++ * snd_ubi32_cs4384_set_rate ++ */ ++static int snd_ubi32_cs4384_set_rate(struct ubi32_snd_priv *priv, int rate) ++{ ++ struct ubi32_cs4384_platform_data *cpd = priv->pdata->priv_data; ++ struct ubicom32_io_port *ioa = (struct ubicom32_io_port *)RA; ++ struct ubicom32_io_port *ioc = (struct ubicom32_io_port *)RC; ++ struct ubicom32_io_port *ioh = (struct ubicom32_io_port *)RH; ++ unsigned int ctl; ++ unsigned int div = 0; ++ const u16_t mult[] = {64, 96, 128, 192, 256, 384, 512, 768, 1024}; ++ int i; ++ int j; ++ ++ ++ for (i = 0; i < sizeof(mult) / sizeof(u16_t); i++) { ++ for (j = 0; j < cpd->n_mclk; j++) { ++ if (((unsigned int)rate * (unsigned int)mult[i]) == ++ cpd->mclk_entries[j].rate) { ++ div = cpd->mclk_entries[j].div; ++ break; ++ } ++ } ++ } ++ ++ ctl = ((div - 1) << 16) | (div / 2); ++ ++ switch (cpd->mclk_src) { ++ case UBI32_CS4384_MCLK_PWM_0: ++ ioc->ctl1 = ctl; ++ return 0; ++ ++ case UBI32_CS4384_MCLK_PWM_1: ++ ioc->ctl2 = ctl; ++ return 0; ++ ++ case UBI32_CS4384_MCLK_PWM_2: ++ ioh->ctl1 = ctl; ++ return 0; ++ ++ case UBI32_CS4384_MCLK_CLKDIV_1: ++ ioa->ctl1 &= ~(0x7F << 14); ++ ioa->ctl1 |= ((div - 1) << 14); ++ return 0; ++ ++ case UBI32_CS4384_MCLK_OTHER: ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/* ++ * snd_ubi32_cs4384_set_channels ++ * Mute unused channels ++ */ ++static int snd_ubi32_cs4384_set_channels(struct ubi32_snd_priv *priv, int channels) ++{ ++ struct i2c_client *client = (struct i2c_client *)priv->client; ++ struct snd_ubi32_cs4384_priv *cs4384_priv; ++ unsigned char send[2]; ++ int ret; ++ int i; ++ unsigned long flags; ++ ++ /* ++ * Only support 0, 2, 4, 6, 8 channels ++ */ ++ if ((channels > 8) || (channels & 1)) { ++ return -EINVAL; ++ } ++ ++ cs4384_priv = snd_ubi32_priv_get_drv(priv); ++ spin_lock_irqsave(&cs4384_priv->lock, flags); ++ ++ /* ++ * Address 09h, Mute control ++ */ ++ send[0] = 0x09; ++ send[1] = (unsigned char)(0xFF << channels); ++ ++ ret = i2c_master_send(client, send, 2); ++ ++ spin_unlock_irqrestore(&cs4384_priv->lock, flags); ++ ++ /* ++ * Notify the system that we changed the mutes ++ */ ++ cs4384_priv->mute = (unsigned char)(0xFF << channels); ++ ++ for (i = SND_UBI32_MUTE_CTL_START; i < SND_UBI32_MUTE_CTL_END; i++) { ++ snd_ctl_notify(priv->card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &cs4384_priv->kctls[i]->id); ++ } ++ ++ if (ret != 2) { ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++/* ++ * snd_ubi32_cs4384_dac_init ++ */ ++static int snd_ubi32_cs4384_dac_init(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ int ret; ++ unsigned char send[2]; ++ unsigned char recv[2]; ++ ++ /* ++ * Initialize the CS4384 DAC over the I2C interface ++ */ ++ snd_printk(KERN_INFO "Initializing CS4384 DAC\n"); ++ ++ /* ++ * Register 0x01: device/revid ++ */ ++ send[0] = 0x01; ++ ret = i2c_master_send(client, send, 1); ++ if (ret != 1) { ++ snd_printk(KERN_ERR "Failed 1st attempt to write to CS4384 register 0x01\n"); ++ goto fail; ++ } ++ ret = i2c_master_recv(client, recv, 1); ++ if (ret != 1) { ++ snd_printk(KERN_ERR "Failed initial read of CS4384 registers\n"); ++ goto fail; ++ } ++ snd_printk(KERN_INFO "CS4384 DAC Device/Rev: %08x\n", recv[0]); ++ ++ /* ++ * Register 0x02: Mode Control 1 ++ * Control Port Enable, PCM, All DACs enabled, Power Down ++ */ ++ send[0] = 0x02; ++ send[1] = 0x81; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to set CPEN CS4384\n"); ++ goto fail; ++ } ++ ++ /* ++ * Register 0x08: Ramp and Mute ++ * RMP_UP, RMP_DN, PAMUTE, DAMUTE ++ */ ++ send[0] = 0x08; ++ send[1] = 0xBC; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to set CPEN CS4384\n"); ++ goto fail; ++ } ++ ++ /* ++ * Register 0x03: PCM Control ++ * I2S DIF[3:0] = 0001, no De-Emphasis, Auto speed mode ++ */ ++ send[0] = 0x03; ++ send[1] = 0x13; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to set CS4384 to I2S mode\n"); ++ goto fail; ++ } ++ ++ /* ++ * Register 0x0B/0x0C: Volume control A1/B1 ++ * Register 0x0E/0x0F: Volume control A2/B2 ++ * Register 0x11/0x12: Volume control A3/B3 ++ * Register 0x14/0x15: Volume control A4/B4 ++ */ ++ send[0] = 0x80 | 0x0B; ++ send[1] = 0x00; ++ send[2] = 0x00; ++ ret = i2c_master_send(client, send, 3); ++ if (ret != 3) { ++ snd_printk(KERN_ERR "Failed to set ch1 volume on CS4384\n"); ++ goto fail; ++ } ++ ++ send[0] = 0x80 | 0x0E; ++ send[1] = 0x00; ++ send[2] = 0x00; ++ ret = i2c_master_send(client, send, 3); ++ if (ret != 3) { ++ snd_printk(KERN_ERR "Failed to set ch2 volume on CS4384\n"); ++ goto fail; ++ } ++ ++ send[0] = 0x80 | 0x11; ++ send[1] = 0x00; ++ send[2] = 0x00; ++ ret = i2c_master_send(client, send, 3); ++ if (ret != 3) { ++ snd_printk(KERN_ERR "Failed to set ch3 volume on CS4384\n"); ++ goto fail; ++ } ++ ++ send[0] = 0x80 | 0x14; ++ send[1] = 0x00; ++ send[2] = 0x00; ++ ret = i2c_master_send(client, send, 3); ++ if (ret != 3) { ++ snd_printk(KERN_ERR "Failed to set ch4 volume on CS4384\n"); ++ goto fail; ++ } ++ ++ /* ++ * Register 09h: Mute control ++ * Mute all (we will unmute channels as needed) ++ */ ++ send[0] = 0x09; ++ send[1] = 0xFF; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to power up CS4384\n"); ++ goto fail; ++ } ++ ++ /* ++ * Register 0x02: Mode Control 1 ++ * Control Port Enable, PCM, All DACs enabled, Power Up ++ */ ++ send[0] = 0x02; ++ send[1] = 0x80; ++ ret = i2c_master_send(client, send, 2); ++ if (ret != 2) { ++ snd_printk(KERN_ERR "Failed to power up CS4384\n"); ++ goto fail; ++ } ++ ++ /* ++ * Make sure the changes took place, this helps verify we are talking to ++ * the correct chip. ++ */ ++ send[0] = 0x80 | 0x03; ++ ret = i2c_master_send(client, send, 1); ++ if (ret != 1) { ++ snd_printk(KERN_ERR "Failed to initiate readback\n"); ++ goto fail; ++ } ++ ++ ret = i2c_master_recv(client, recv, 1); ++ if (ret != 1) { ++ snd_printk(KERN_ERR "Failed second read of CS4384 registers\n"); ++ goto fail; ++ } ++ ++ if (recv[0] != 0x13) { ++ snd_printk(KERN_ERR "Failed to initialize CS4384 DAC\n"); ++ goto fail; ++ } ++ ++ snd_printk(KERN_INFO "CS4384 DAC Initialized\n"); ++ return 0; ++ ++fail: ++ return -ENODEV; ++} ++ ++/* ++ * snd_ubi32_cs4384_i2c_probe ++ */ ++static int snd_ubi32_cs4384_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ struct snd_card *card; ++ struct ubi32_snd_priv *ubi32_priv; ++ int err, ret; ++ struct platform_device *pdev; ++ struct ubi32_cs4384_platform_data *pdata; ++ struct snd_ubi32_cs4384_priv *cs4384_priv; ++ ++ /* ++ * pdev is audio_tio ++ */ ++ pdev = client->dev.platform_data; ++ if (!pdev) { ++ return -ENODEV; ++ } ++ ++ /* ++ * pdev->dev.platform_data is ubi32-pcm platform_data ++ */ ++ pdata = audio_tio_priv(pdev); ++ if (!pdata) { ++ return -ENODEV; ++ } ++ ++ /* ++ * Initialize the CS4384 DAC ++ */ ++ ret = snd_ubi32_cs4384_dac_init(client, id); ++ if (ret < 0) { ++ /* ++ * Initialization failed. Propagate the error. ++ */ ++ return ret; ++ } ++ ++ if (snd_ubi32_cs4384_setup_mclk(pdata)) { ++ return -EINVAL; ++ } ++ ++ /* ++ * Create a snd_card structure ++ */ ++ card = snd_card_new(index, "Ubi32-CS4384", THIS_MODULE, sizeof(struct ubi32_snd_priv)); ++ if (card == NULL) { ++ return -ENOMEM; ++ } ++ ++ card->private_free = snd_ubi32_cs4384_free; ++ ubi32_priv = card->private_data; ++ ++ /* ++ * Initialize the snd_card's private data structure ++ */ ++ ubi32_priv->card = card; ++ ubi32_priv->client = client; ++ ubi32_priv->set_channels = snd_ubi32_cs4384_set_channels; ++ ubi32_priv->set_rate = snd_ubi32_cs4384_set_rate; ++ ++ /* ++ * CS4384 DAC has a minimum sample rate of 4khz and an ++ * upper limit of 216khz for it's auto-detect. ++ */ ++ ubi32_priv->min_sample_rate = 4000; ++ ubi32_priv->max_sample_rate = 216000; ++ ++ /* ++ * Create our private data (to manage volume, etc) ++ */ ++ cs4384_priv = kzalloc(sizeof(struct snd_ubi32_cs4384_priv), GFP_KERNEL); ++ if (!cs4384_priv) { ++ snd_card_free(card); ++ return -ENOMEM; ++ } ++ snd_ubi32_priv_set_drv(ubi32_priv, cs4384_priv); ++ spin_lock_init(&cs4384_priv->lock); ++ ++ /* ++ * We start off all muted and max volume ++ */ ++ cs4384_priv->mute = 0xFF; ++ memset(cs4384_priv->volume, 0xFF, 8); ++ ++ /* ++ * Create the new PCM instance ++ */ ++ err = snd_ubi32_pcm_probe(ubi32_priv, pdev); ++ if (err < 0) { ++ snd_card_free(card); ++ return err; /* What is err? Need to include correct file */ ++ } ++ ++ strcpy(card->driver, "Ubi32-CS4384"); ++ strcpy(card->shortname, "Ubi32-CS4384"); ++ sprintf(card->longname, "%s at sendirq=%d recvirq=%d", card->shortname, ubi32_priv->tx_irq, ubi32_priv->rx_irq); ++ ++ snd_card_set_dev(card, &client->dev); ++ ++ /* ++ * Set up the mixer ++ */ ++ snd_ubi32_cs4384_mixer(ubi32_priv); ++ ++ /* ++ * Register the sound card ++ */ ++ if ((err = snd_card_register(card)) != 0) { ++ snd_printk(KERN_INFO "snd_card_register error\n"); ++ } ++ ++ /* ++ * Store card for access from other methods ++ */ ++ i2c_set_clientdata(client, card); ++ ++ return 0; ++} ++ ++/* ++ * snd_ubi32_cs4384_i2c_remove ++ */ ++static int __devexit snd_ubi32_cs4384_i2c_remove(struct i2c_client *client) ++{ ++ struct snd_card *card; ++ struct ubi32_snd_priv *ubi32_priv; ++ ++ card = i2c_get_clientdata(client); ++ ++ ubi32_priv = card->private_data; ++ snd_ubi32_pcm_remove(ubi32_priv); ++ ++ snd_card_free(i2c_get_clientdata(client)); ++ i2c_set_clientdata(client, NULL); ++ ++ return 0; ++} ++ ++/* ++ * I2C driver description ++ */ ++static struct i2c_driver snd_ubi32_cs4384_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .id_table = snd_ubi32_cs4384_id, ++ .probe = snd_ubi32_cs4384_i2c_probe, ++ .remove = __devexit_p(snd_ubi32_cs4384_i2c_remove), ++}; ++ ++/* ++ * Driver init ++ */ ++static int __init snd_ubi32_cs4384_init(void) ++{ ++ return i2c_add_driver(&snd_ubi32_cs4384_driver); ++} ++module_init(snd_ubi32_cs4384_init); ++ ++/* ++ * snd_ubi32_cs4384_exit ++ */ ++static void __exit snd_ubi32_cs4384_exit(void) ++{ ++ i2c_del_driver(&snd_ubi32_cs4384_driver); ++} ++module_exit(snd_ubi32_cs4384_exit); ++ ++/* ++ * Module properties ++ */ ++MODULE_ALIAS("i2c:" DRIVER_NAME); ++MODULE_AUTHOR("Patrick Tjin"); ++MODULE_DESCRIPTION("Driver for Ubicom32 audio devices CS4384"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/sound/ubicom32/ubi32-generic.c +@@ -0,0 +1,165 @@ ++/* ++ * sound/ubicom32/ubi32-generic.c ++ * Interface to ubicom32 virtual audio peripheral ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/initval.h> ++#include "ubi32.h" ++ ++#define DRIVER_NAME "snd-ubi32-generic" ++ ++/* ++ * Module properties ++ */ ++static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ ++ ++/* ++ * Card private data free function ++ */ ++void snd_ubi32_generic_free(struct snd_card *card) ++{ ++ //struct ubi32_snd_priv *ubi32_priv = card->private_data; ++ ++ /* ++ * Free all the fields in the snd_ubi32_priv struct ++ */ ++ // Nothing to free at this time because ubi32_priv just maintains pointers ++} ++ ++/* ++ * Ubicom audio driver probe() method. Args change depending on whether we use ++ * platform_device or i2c_device. ++ */ ++static int snd_ubi32_generic_probe(struct platform_device *dev) ++{ ++ struct snd_card *card; ++ struct ubi32_snd_priv *ubi32_priv; ++ int err; ++ ++ /* ++ * Create a snd_card structure ++ */ ++ card = snd_card_new(index, "Ubi32-Generic", THIS_MODULE, sizeof(struct ubi32_snd_priv)); ++ ++ if (card == NULL) { ++ return -ENOMEM; ++ } ++ ++ card->private_free = snd_ubi32_generic_free; /* Not sure if correct */ ++ ubi32_priv = card->private_data; ++ ++ /* ++ * Initialize the snd_card's private data structure ++ */ ++ ubi32_priv->card = card; ++ ++ /* ++ * Create the new PCM instance ++ */ ++ err = snd_ubi32_pcm_probe(ubi32_priv, dev); ++ if (err < 0) { ++ snd_card_free(card); ++ return err; ++ } ++ ++ strcpy(card->driver, "Ubi32-Generic"); ++ strcpy(card->shortname, "Ubi32-Generic"); ++ sprintf(card->longname, "%s at sendirq=%d recvirq=%d", card->shortname, ubi32_priv->tx_irq, ubi32_priv->rx_irq); ++ ++ snd_card_set_dev(card, &dev->dev); ++ ++ /* Register the sound card */ ++ if ((err = snd_card_register(card)) != 0) { ++ snd_printk(KERN_INFO "snd_card_register error\n"); ++ } ++ ++ /* Store card for access from other methods */ ++ platform_set_drvdata(dev, card); ++ ++ return 0; ++} ++ ++/* ++ * Ubicom audio driver remove() method ++ */ ++static int __devexit snd_ubi32_generic_remove(struct platform_device *dev) ++{ ++ struct snd_card *card; ++ struct ubi32_snd_priv *ubi32_priv; ++ ++ card = platform_get_drvdata(dev); ++ ubi32_priv = card->private_data; ++ snd_ubi32_pcm_remove(ubi32_priv); ++ ++ snd_card_free(platform_get_drvdata(dev)); ++ platform_set_drvdata(dev, NULL); ++ return 0; ++} ++ ++/* ++ * Platform driver definition ++ */ ++static struct platform_driver snd_ubi32_generic_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_ubi32_generic_probe, ++ .remove = __devexit_p(snd_ubi32_generic_remove), ++}; ++ ++/* ++ * snd_ubi32_generic_init ++ */ ++static int __init snd_ubi32_generic_init(void) ++{ ++ return platform_driver_register(&snd_ubi32_generic_driver); ++} ++module_init(snd_ubi32_generic_init); ++ ++/* ++ * snd_ubi32_generic_exit ++ */ ++static void __exit snd_ubi32_generic_exit(void) ++{ ++ platform_driver_unregister(&snd_ubi32_generic_driver); ++} ++module_exit(snd_ubi32_generic_exit); ++ ++/* ++ * Module properties ++ */ ++//#if defined(CONFIG_SND_UBI32_AUDIO_I2C) ++//MODULE_ALIAS("i2c:snd-ubi32"); ++//#endif ++MODULE_AUTHOR("Aaron Jow, Patrick Tjin"); ++MODULE_DESCRIPTION("Driver for Ubicom32 audio devices"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/sound/ubicom32/ubi32.h +@@ -0,0 +1,96 @@ ++/* ++ * sound/ubicom32/ubi32.h ++ * Common header file for all ubi32- sound drivers ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef _UBI32_H ++#define _UBI32_H ++ ++#define SND_UBI32_DEBUG 0 // Debug flag ++ ++#include <linux/platform_device.h> ++#include <asm/devtree.h> ++#include <asm/audio_tio.h> ++#include <asm/ubi32-pcm.h> ++ ++struct ubi32_snd_priv; ++ ++typedef int (*set_channels_t)(struct ubi32_snd_priv *priv, int channels); ++typedef int (*set_rate_t)(struct ubi32_snd_priv *priv, int rate); ++ ++struct ubi32_snd_priv { ++ /* ++ * Any variables that are needed locally here but NOT in ++ * the VP itself should go in here. ++ */ ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ ++ /* ++ * DAC parameters. These are the parameters for the specific ++ * DAC we are driving. The I2S component can run at a range ++ * of frequencies, but the DAC may be limited. We may want ++ * to make this an array of some sort in the future? ++ * ++ * min/max_sample_rate if set to 0 are ignored. ++ */ ++ int max_sample_rate; ++ int min_sample_rate; ++ ++ /* ++ * The size a period (group) of audio samples. The VP does ++ * not need to know this; each DMA transfer is made to be ++ * one period. ++ */ ++ u32_t period_size; ++ ++ spinlock_t ubi32_lock; ++ ++ struct audiotioregs *regs; ++ u8 tx_irq; ++ u8 rx_irq; ++ ++ void *client; ++ ++ /* ++ * Operations which the base DAC driver can implement ++ */ ++ set_channels_t set_channels; ++ set_rate_t set_rate; ++ ++ /* ++ * platform data ++ */ ++ struct ubi32pcm_platform_data *pdata; ++ ++ /* ++ * Private driver data (used for DAC driver control, etc) ++ */ ++ void *drvdata; ++}; ++ ++#define snd_ubi32_priv_get_drv(priv) ((priv)->drvdata) ++#define snd_ubi32_priv_set_drv(priv, data) (((priv)->drvdata) = (void *)(data)) ++ ++extern int snd_ubi32_pcm_probe(struct ubi32_snd_priv *ubi32_priv, struct platform_device *pdev); ++extern void snd_ubi32_pcm_remove(struct ubi32_snd_priv *ubi32_priv); ++ ++#endif +--- /dev/null ++++ b/sound/ubicom32/ubi32-pcm.c +@@ -0,0 +1,680 @@ ++/* ++ * sound/ubicom32/ubi32-pcm.c ++ * Interface to ubicom32 virtual audio peripheral ++ * ++ * (C) Copyright 2009, Ubicom, Inc. ++ * ++ * This file is part of the Ubicom32 Linux Kernel Port. ++ * ++ * The Ubicom32 Linux Kernel Port is free software: you can redistribute ++ * it and/or modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation, either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Ubicom32 Linux Kernel Port is distributed in the hope that it ++ * will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with the Ubicom32 Linux Kernel Port. If not, ++ * see <http://www.gnu.org/licenses/>. ++ * ++ * Ubicom32 implementation derived from (with many thanks): ++ * arch/m68knommu ++ * arch/blackfin ++ * arch/parisc ++ */ ++ ++#include <linux/interrupt.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/pcm_params.h> ++#include <asm/ip5000.h> ++#include <asm/ubi32-pcm.h> ++#include <linux/dma-mapping.h> ++#include <linux/delay.h> ++#include "ubi32.h" ++ ++struct ubi32_snd_runtime_data { ++ dma_addr_t dma_buffer; /* Physical address of DMA buffer */ ++ dma_addr_t dma_buffer_end; /* First address beyond end of DMA buffer */ ++ size_t period_size; ++ dma_addr_t period_ptr; /* Physical address of next period */ ++ unsigned int flags; ++}; ++ ++static void snd_ubi32_vp_int_set(struct snd_pcm *pcm) ++{ ++ struct ubi32_snd_priv *ubi32_priv = pcm->private_data; ++ ubicom32_set_interrupt(ubi32_priv->tx_irq); ++} ++ ++static snd_pcm_uframes_t snd_ubi32_playback_pointer(struct snd_pcm_substream *substream) ++{ ++ struct ubi32_snd_priv *ubi32_priv = snd_pcm_substream_chip(substream); ++ struct audiotioregs *regs = ubi32_priv->regs; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; ++ ++ dma_addr_t read_pos; ++ ++ snd_pcm_uframes_t frames; ++ if (!regs->current_read_pos) { ++ /* ++ * If current_read_pos is NULL (e.g. right after the HW is started or ++ * when the HW is stopped), then handle this case separately. ++ */ ++ return 0; ++ } ++ ++ read_pos = (dma_addr_t)regs->current_read_pos; ++ frames = bytes_to_frames(runtime, read_pos - ubi32_rd->dma_buffer); ++ if (frames == runtime->buffer_size) { ++ frames = 0; ++ } ++ return frames; ++} ++ ++/* ++ * Audio trigger ++ */ ++static int snd_ubi32_playback_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data; ++ struct audiotioregs *regs = ubi32_priv->regs; ++ struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; ++ int ret = 0; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "snd_ubi32_playback_trigger cmd=%d=", cmd); ++#endif ++ ++ if (regs->command != UBI32_AUDIO_CMD_NONE) { ++ snd_printk(KERN_WARNING "Can't send command to audio device at this time\n"); ++ // Set a timer to call this function back later. How to do this? ++ return 0; ++ } ++ ++ /* ++ * Set interrupt flag to indicate that we interrupted audio device ++ * to send a command ++ */ ++ //regs->int_flags |= UBI32_AUDIO_INT_FLAG_COMMAND; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "START\n"); ++#endif ++ /* ++ * Ready the DMA transfer ++ */ ++ ubi32_rd->period_ptr = ubi32_rd->dma_buffer; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "trigger period_ptr=%lx\n", (unsigned long)ubi32_rd->period_ptr); ++#endif ++ regs->playback_xfer_requests[0].ptr = (void *)ubi32_rd->period_ptr; ++ regs->playback_xfer_requests[0].ctr = ubi32_rd->period_size; ++ regs->playback_xfer_requests[0].active = 1; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "xfer_request 0 ptr=0x%x ctr=%u\n", ubi32_rd->period_ptr, ubi32_rd->period_size); ++#endif ++ ++ ubi32_rd->period_ptr += ubi32_rd->period_size; ++ regs->playback_xfer_requests[1].ptr = (void *)ubi32_rd->period_ptr; ++ regs->playback_xfer_requests[1].ctr = ubi32_rd->period_size; ++ regs->playback_xfer_requests[1].active = 1; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "xfer_request 1 ptr=0x%x ctr=%u\n", ubi32_rd->period_ptr, ubi32_rd->period_size); ++#endif ++ ++ /* ++ * Tell the VP that we want to begin playback by filling in the ++ * command field and then interrupting the audio VP ++ */ ++ regs->int_flags |= UBI32_AUDIO_INT_FLAG_COMMAND; ++ regs->command = UBI32_AUDIO_CMD_START; ++ snd_ubi32_vp_int_set(substream->pcm); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "STOP\n"); ++#endif ++ ++ /* ++ * Tell the VP that we want to stop playback by filling in the ++ * command field and then interrupting the audio VP ++ */ ++ regs->int_flags |= UBI32_AUDIO_INT_FLAG_COMMAND; ++ regs->command = UBI32_AUDIO_CMD_STOP; ++ snd_ubi32_vp_int_set(substream->pcm); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "PAUSE_PUSH\n"); ++#endif ++ ++ /* ++ * Tell the VP that we want to pause playback by filling in the ++ * command field and then interrupting the audio VP ++ */ ++ regs->int_flags |= UBI32_AUDIO_INT_FLAG_COMMAND; ++ regs->command = UBI32_AUDIO_CMD_PAUSE; ++ snd_ubi32_vp_int_set(substream->pcm); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "PAUSE_RELEASE\n"); ++#endif ++ /* ++ * Tell the VP that we want to resume paused playback by filling ++ * in the command field and then interrupting the audio VP ++ */ ++ regs->int_flags |= UBI32_AUDIO_INT_FLAG_COMMAND; ++ regs->command = UBI32_AUDIO_CMD_RESUME; ++ snd_ubi32_vp_int_set(substream->pcm); ++ break; ++ ++ default: ++ snd_printk(KERN_WARNING "Unhandled trigger\n"); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Prepare to transfer an audio stream to the codec ++ */ ++static int snd_ubi32_playback_prepare(struct snd_pcm_substream *substream) ++{ ++ /* ++ * Configure registers and setup the runtime instance for DMA transfers ++ */ ++ //struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; ++ struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data; ++ struct audiotioregs *regs = ubi32_priv->regs; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "snd_ubi32_playback_prepare: sending STOP command to audio device\n"); ++#endif ++ ++ /* ++ * Make sure the audio device is stopped ++ */ ++ ++ /* ++ * Set interrupt flag to indicate that we interrupted audio device ++ * to send a command ++ */ ++ regs->int_flags |= UBI32_AUDIO_INT_FLAG_COMMAND; ++ regs->command = UBI32_AUDIO_CMD_STOP; ++ snd_ubi32_vp_int_set(substream->pcm); ++ ++ return 0; ++} ++ ++/* ++ * Allocate DMA buffers from preallocated memory. ++ * Preallocation was done in snd_ubi32_pcm_new() ++ */ ++static int snd_ubi32_playback_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data; ++ struct audiotioregs *regs = ubi32_priv->regs; ++ struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; ++ ++ /* ++ * Use pre-allocated memory from ubi32_snd_pcm_new() to satisfy ++ * this memory request. ++ */ ++ int ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "snd_ubi32_playback_hw_params\n"); ++#endif ++ ++ if (!(regs->channel_mask & (1 << params_channels(hw_params)))) { ++ snd_printk(KERN_INFO "snd_ubi32_playback_hw_params unsupported number of channels %d mask %08x\n", params_channels(hw_params), regs->channel_mask); ++ return -EINVAL; ++ } ++ ++ if (ubi32_priv->set_channels) { ++ int ret = ubi32_priv->set_channels(ubi32_priv, params_channels(hw_params)); ++ if (ret) { ++ snd_printk(KERN_WARNING "Unable to set channels to %d, ret=%d\n", params_channels(hw_params), ret); ++ return ret; ++ } ++ } ++ ++ if (ubi32_priv->set_rate) { ++ int ret = ubi32_priv->set_rate(ubi32_priv, params_rate(hw_params)); ++ if (ret) { ++ snd_printk(KERN_WARNING "Unable to set rate to %d, ret=%d\n", params_rate(hw_params), ret); ++ return ret; ++ } ++ } ++ ++ if (ubi32_priv->pdata->set_rate) { ++ int ret = ubi32_priv->pdata->set_rate(ubi32_priv->pdata->appdata, params_rate(hw_params)); ++ if (ret) { ++ snd_printk(KERN_WARNING "Unable to set rate to %d, ret=%d\n", params_rate(hw_params), ret); ++ return ret; ++ } ++ } ++ ++ if (regs->command != UBI32_AUDIO_CMD_NONE) { ++ snd_printk(KERN_WARNING "snd_ubi32_playback_hw_params: tio busy\n"); ++ return -EAGAIN; ++ } ++ ++ if (params_format(hw_params) == SNDRV_PCM_FORMAT_S16_LE) { ++ regs->flags |= UBI32_CMD_SETUP_FLAG_LE; ++ } else { ++ regs->flags &= ~UBI32_CMD_SETUP_FLAG_LE; ++ } ++ regs->channels = params_channels(hw_params); ++ regs->sample_rate = params_rate(hw_params); ++ regs->command = UBI32_AUDIO_CMD_SETUP; ++ regs->int_flags |= UBI32_AUDIO_INT_FLAG_COMMAND; ++ snd_ubi32_vp_int_set(substream->pcm); ++ ++ /* ++ * Wait for the command to complete ++ */ ++ while (regs->command != UBI32_AUDIO_CMD_NONE) { ++ udelay(1); ++ } ++ ++ //snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); ++ //runtime->dma_bytes = params_buffer_bytes(hw_params); ++ ++ /* ++ * Put the DMA info into the DMA descriptor that we will ++ * use to do transfers to our audio VP "hardware" ++ */ ++ ++ /* ++ * Mark both DMA transfers as not ready/inactive ++ */ ++ regs->playback_xfer_requests[0].active = 0; ++ regs->playback_xfer_requests[1].active = 0; ++ ++ /* ++ * Put the location of the buffer into the runtime data instance ++ */ ++ ubi32_rd->dma_buffer = (dma_addr_t)runtime->dma_area; ++ ubi32_rd->dma_buffer_end = (dma_addr_t)(runtime->dma_area + runtime->dma_bytes); ++ ++ /* ++ * Get the period size ++ */ ++ ubi32_rd->period_size = params_period_bytes(hw_params); ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "DMA for ubi32 audio initialized dma_area=0x%x dma_bytes=%d, period_size=%d\n", (unsigned int)runtime->dma_area, (unsigned int)runtime->dma_bytes, ubi32_rd->period_size); ++ snd_printk(KERN_INFO "Private buffer ubi32_rd: dma_buffer=0x%x dma_buffer_end=0x%x\n", ubi32_rd->dma_buffer, ubi32_rd->dma_buffer_end); ++#endif ++ ++ return ret; ++} ++ ++/* ++ * This is the reverse of snd_ubi32_playback_hw_params ++ */ ++static int snd_ubi32_playback_hw_free(struct snd_pcm_substream *substream) ++{ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "snd_ubi32_playback_hw_free\n"); ++#endif ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++/* ++ * Audio virtual peripheral capabilities ++ */ ++static struct snd_pcm_hardware snd_ubi32_playback_hw = ++{ ++ /* FILL IN THIS STRUCT WITH HW PARAMS SPECIFIC TO IPI2SOUT */ ++ // This is defined in include/sound/asound.h or ++ // include/sound/pcm.h ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), // NOT SURE ABOUT THIS ++ //.info = (SNDRV_PCM_INFO_INTERLEAVED | ++ // SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ // SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | ++ // SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), ++ .buffer_bytes_max = (64*1024), // NOT SURE ABOUT THIS ++ .period_bytes_min = 64, ++ .period_bytes_max = 8184,//8184,//8176, ++ .periods_min = 2, ++ .periods_max = 255, // THIS WILL ALMOST CERTAINLY NEED TO BE CHANGED ++ .fifo_size = 0, // THIS IS IGNORED BY ALSA ++}; ++ ++/* ++ * We fill this in later ++ */ ++static struct snd_pcm_hw_constraint_list ubi32_playback_rates; ++ ++static int snd_ubi32_playback_close(struct snd_pcm_substream *substream) ++{ ++ /* Disable codec, stop DMA, free private data structures */ ++ //struct ubi32_snd_priv *ubi32_priv = snd_pcm_substream_chip(substream); ++ struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "snd_ubi32_playback_close\n"); ++#endif ++ ++ substream->runtime->private_data = NULL; ++ ++ kfree(ubi32_rd); ++ ++ return 0; ++} ++ ++static int snd_ubi32_playback_open(struct snd_pcm_substream *substream) ++{ ++ //struct ubi32_snd_priv *ubi32_priv = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct ubi32_snd_runtime_data *ubi32_rd; ++ int ret = 0; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "ubi32 playback open\n"); ++#endif ++ ++ /* Associate capabilities with component */ ++ runtime->hw = snd_ubi32_playback_hw; ++ ++ /* ++ * Inform ALSA about constraints of the audio device ++ */ ++ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &ubi32_playback_rates); ++ if (ret < 0) { ++ snd_printk(KERN_INFO "invalid rate\n"); ++ goto out; ++ } ++ ++ /* Force the buffer size to be an integer multiple of period size */ ++ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); ++ if (ret < 0) { ++ snd_printk(KERN_INFO "invalid period\n"); ++ goto out; ++ } ++ /* Initialize structures/registers */ ++ ubi32_rd = kzalloc(sizeof(struct ubi32_snd_runtime_data), GFP_KERNEL); ++ if (ubi32_rd == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ runtime->private_data = ubi32_rd; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "snd_ubi32_playback_open returned 0\n"); ++#endif ++ ++ return 0; ++out: ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "snd_ubi32_playback_open returned %d\n", ret); ++#endif ++ ++ return ret; ++} ++ ++static struct snd_pcm_ops snd_ubi32_playback_ops = { ++ .open = snd_ubi32_playback_open, /* Open */ ++ .close = snd_ubi32_playback_close, /* Close */ ++ .ioctl = snd_pcm_lib_ioctl, /* Generic IOCTL handler */ ++ .hw_params = snd_ubi32_playback_hw_params, /* Hardware parameters/capabilities */ ++ .hw_free = snd_ubi32_playback_hw_free, /* Free function for hw_params */ ++ .prepare = snd_ubi32_playback_prepare, ++ .trigger = snd_ubi32_playback_trigger, ++ .pointer = snd_ubi32_playback_pointer, ++}; ++ ++/* ++ * Interrupt handler that gets called when the audio device ++ * interrupts Linux ++ */ ++static irqreturn_t snd_ubi32_pcm_interrupt(int irq, void *appdata) ++{ ++ struct snd_pcm *pcm = (struct snd_pcm *)appdata; ++ struct ubi32_snd_priv *ubi32_priv = pcm->private_data; ++ struct audiotioregs *regs = ubi32_priv->regs; ++ ++ // Only one substream exists because our VP does not mix ++ // streams. Therefore we don't have to search through ++ // substreams like the at91 driver does. ++ struct snd_pcm_substream *substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; ++ struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; ++ int dma_to_fill = 0; ++ ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "Ubi32 ALSA interrupt\n"); ++#endif ++ ++ if (!pcm) { ++ snd_printk(KERN_WARNING "pcm does not exist\n"); ++ return IRQ_HANDLED; ++ } ++ ++ if (ubi32_rd == NULL) { ++ snd_printk(KERN_WARNING "No runtime data\n"); ++ return IRQ_HANDLED; ++ } ++ // Check interrupt cause ++ if (0) { ++ // Handle the underflow case ++ } else if ((regs->status & UBI32_AUDIO_STATUS_PLAY_DMA0_REQUEST) || ++ (regs->status & UBI32_AUDIO_STATUS_PLAY_DMA1_REQUEST)) { ++ if (regs->status & UBI32_AUDIO_STATUS_PLAY_DMA0_REQUEST) { ++ dma_to_fill = 0; ++ regs->status &= ~UBI32_AUDIO_STATUS_PLAY_DMA0_REQUEST; ++ } else if (regs->status & UBI32_AUDIO_STATUS_PLAY_DMA1_REQUEST) { ++ dma_to_fill = 1; ++ regs->status &= ~UBI32_AUDIO_STATUS_PLAY_DMA1_REQUEST; ++ } ++ ubi32_rd->period_ptr += ubi32_rd->period_size; ++ if (ubi32_rd->period_ptr >= ubi32_rd->dma_buffer_end) { ++ ubi32_rd->period_ptr = ubi32_rd->dma_buffer; ++ } ++ regs->playback_xfer_requests[dma_to_fill].ptr = (void *)ubi32_rd->period_ptr; ++ regs->playback_xfer_requests[dma_to_fill].ctr = ubi32_rd->period_size; ++ regs->playback_xfer_requests[dma_to_fill].active = 1; ++#if SND_UBI32_DEBUG ++ snd_printk(KERN_INFO "xfer_request %d ptr=0x%x ctr=%u\n", dma_to_fill, ubi32_rd->period_ptr, ubi32_rd->period_size); ++#endif ++ regs->int_flags |= UBI32_AUDIO_INT_FLAG_MORE_SAMPLES; ++ snd_ubi32_vp_int_set(substream->pcm); ++ } ++ // If we are interrupted by the VP, that means we completed ++ // processing one period of audio. We need to inform the upper ++ // layers of ALSA of this. ++ snd_pcm_period_elapsed(substream); ++ ++ return IRQ_HANDLED; ++} ++ ++void __devexit snd_ubi32_pcm_remove(struct ubi32_snd_priv *ubi32_priv) ++{ ++ struct snd_pcm *pcm = ubi32_priv->pcm; ++ free_irq(ubi32_priv->rx_irq, pcm); ++} ++ ++#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 ++#error "Change this table to match pcm.h" ++#endif ++static unsigned int rates[] __initdata = {5512, 8000, 11025, 16000, 22050, ++ 32000, 44100, 48000, 64000, 88200, ++ 96000, 176400, 192000}; ++ ++/* ++ * snd_ubi32_pcm_probe ++ */ ++int __devinit snd_ubi32_pcm_probe(struct ubi32_snd_priv *ubi32_priv, struct platform_device *pdev) ++{ ++ struct snd_pcm *pcm; ++ int ret, err; ++ int i; ++ int j; ++ int nrates; ++ unsigned int rate_max = 0; ++ unsigned int rate_min = 0xFFFFFFFF; ++ unsigned int rate_mask = 0; ++ struct audiotioregs *regs; ++ struct resource *res_regs; ++ struct resource *res_irq_tx; ++ struct resource *res_irq_rx; ++ struct ubi32pcm_platform_data *pdata; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ return -ENODEV; ++ } ++ ++ /* ++ * Get our resources, regs is the hardware driver base address ++ * and the tx and rx irqs are used to communicate with the ++ * hardware driver. ++ */ ++ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, AUDIOTIO_MEM_RESOURCE); ++ res_irq_tx = platform_get_resource(pdev, IORESOURCE_IRQ, AUDIOTIO_TX_IRQ_RESOURCE); ++ res_irq_rx = platform_get_resource(pdev, IORESOURCE_IRQ, AUDIOTIO_RX_IRQ_RESOURCE); ++ if (!res_regs || !res_irq_tx || !res_irq_rx) { ++ snd_printk(KERN_WARNING "Could not get resources"); ++ return -ENODEV; ++ } ++ ++ ubi32_priv->regs = (struct audiotioregs *)res_regs->start; ++ ubi32_priv->tx_irq = res_irq_tx->start; ++ ubi32_priv->rx_irq = res_irq_rx->start; ++ ++ /* ++ * Check the version ++ */ ++ regs = ubi32_priv->regs; ++ if (regs->version != AUDIOTIOREGS_VERSION) { ++ snd_printk(KERN_WARNING "This audiotioreg is not compatible with this driver\n"); ++ return -ENODEV; ++ } ++ ++ /* ++ * Find out the standard rates, also find max and min rates ++ */ ++ for (i = 0; i < ARRAY_SIZE(rates); i++) { ++ int found = 0; ++ for (j = 0; j < regs->n_sample_rates; j++) { ++ if (rates[i] == regs->sample_rates[j]) { ++ /* ++ * Check to see if it is supported by the dac ++ */ ++ if ((rates[i] >= ubi32_priv->min_sample_rate) && ++ (!ubi32_priv->max_sample_rate || ++ (ubi32_priv->max_sample_rate && (rates[i] <= ubi32_priv->max_sample_rate)))) { ++ found = 1; ++ rate_mask |= (1 << i); ++ nrates++; ++ if (rates[i] < rate_min) { ++ rate_min = rates[i]; ++ } ++ if (rates[i] > rate_max) { ++ rate_max = rates[i]; ++ } ++ break; ++ } ++ } ++ } ++ if (!found) { ++ rate_mask |= SNDRV_PCM_RATE_KNOT; ++ } ++ } ++ ++ snd_ubi32_playback_hw.rates = rate_mask; ++ snd_ubi32_playback_hw.rate_min = rate_min; ++ snd_ubi32_playback_hw.rate_max = rate_max; ++ ubi32_playback_rates.count = regs->n_sample_rates; ++ ubi32_playback_rates.list = regs->sample_rates; ++ ubi32_playback_rates.mask = 0; ++ ++ for (i = 0; i < 32; i++) { ++ if (regs->channel_mask & (1 << i)) { ++ if (!snd_ubi32_playback_hw.channels_min) { ++ snd_ubi32_playback_hw.channels_min = i; ++ } ++ snd_ubi32_playback_hw.channels_max = i; ++ } ++ } ++ snd_printk(KERN_INFO "Ubi32PCM: channels_min:%u channels_max:%u\n", ++ snd_ubi32_playback_hw.channels_min, ++ snd_ubi32_playback_hw.channels_max); ++ ++ if (regs->caps & AUDIOTIONODE_CAP_BE) { ++ snd_ubi32_playback_hw.formats |= SNDRV_PCM_FMTBIT_S16_BE; ++ } ++ if (regs->caps & AUDIOTIONODE_CAP_LE) { ++ snd_ubi32_playback_hw.formats |= SNDRV_PCM_FMTBIT_S16_LE; ++ } ++ snd_printk(KERN_INFO "Ubi32PCM: rates:%08x min:%u max:%u count:%d fmts:%016llx\n", ++ snd_ubi32_playback_hw.rates, ++ snd_ubi32_playback_hw.rate_min, ++ snd_ubi32_playback_hw.rate_max, ++ ubi32_playback_rates.count, ++ snd_ubi32_playback_hw.formats); ++ ++ ret = snd_pcm_new(ubi32_priv->card, "Ubi32 PCM", 0, 1, 0, &pcm); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ pcm->private_data = ubi32_priv; ++ ubi32_priv->pcm = pcm; ++ ubi32_priv->pdata = pdata; ++ ++ pcm->info_flags = 0; // What is this for? Just copied from other drivers ++ ++ strcpy(pcm->name, "Ubi32-PCM"); ++ ++ // How do we allocate the buffer for non PCI devices? ++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, ++ snd_dma_continuous_data(GFP_KERNEL), ++ 45*1024, 64*1024); //NOT SURE ABOUT SIZE, CHECK LATER ++ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ubi32_playback_ops); ++ ++ /* ++ * Start up the TIO ++ */ ++ regs->int_flags |= UBI32_AUDIO_INT_FLAG_COMMAND; ++ regs->command = UBI32_AUDIO_CMD_ENABLE; ++ snd_ubi32_vp_int_set(pcm); ++ ++ /* ++ * Request IRQ ++ */ ++ err = request_irq(ubi32_priv->rx_irq, snd_ubi32_pcm_interrupt, IRQF_DISABLED, pcm->name, pcm); ++ if (err) { ++ snd_printk(KERN_WARNING "request_irq failed: irq=%d err=%d\n", ubi32_priv->rx_irq, err); ++ return -ENODEV; ++ } ++ ++ return ret; ++ ++} +--- /dev/null ++++ b/arch/ubicom32/lib/ashldi3.c +@@ -0,0 +1,62 @@ ++/* ashrdi3.c extracted from gcc-2.95.2/libgcc2.c which is: */ ++/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. ++ ++This file is part of GNU CC. ++ ++GNU CC is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2, or (at your option) ++any later version. ++ ++GNU CC is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with GNU CC; see the file COPYING. If not, write to ++the Free Software Foundation, 59 Temple Place - Suite 330, ++Boston, MA 02111-1307, USA. */ ++ ++#define BITS_PER_UNIT 8 ++ ++typedef int SItype __attribute__ ((mode (SI))); ++typedef unsigned int USItype __attribute__ ((mode (SI))); ++typedef int DItype __attribute__ ((mode (DI))); ++typedef int word_type __attribute__ ((mode (__word__))); ++ ++struct DIstruct {SItype high, low;}; ++ ++typedef union ++{ ++ struct DIstruct s; ++ DItype ll; ++} DIunion; ++ ++DItype ++__ashldi3 (DItype u, word_type b) ++{ ++ DIunion w; ++ word_type bm; ++ DIunion uu; ++ ++ if (b == 0) ++ return u; ++ ++ uu.ll = u; ++ ++ bm = (sizeof (SItype) * BITS_PER_UNIT) - b; ++ if (bm <= 0) ++ { ++ w.s.low = 0; ++ w.s.high = (USItype)uu.s.low << -bm; ++ } ++ else ++ { ++ USItype carries = (USItype)uu.s.low >> bm; ++ w.s.low = (USItype)uu.s.low << b; ++ w.s.high = ((USItype)uu.s.high << b) | carries; ++ } ++ ++ return w.ll; ++} +--- /dev/null ++++ b/arch/ubicom32/lib/ashrdi3.c +@@ -0,0 +1,63 @@ ++/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ ++/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. ++ ++This file is part of GNU CC. ++ ++GNU CC is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2, or (at your option) ++any later version. ++ ++GNU CC is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with GNU CC; see the file COPYING. If not, write to ++the Free Software Foundation, 59 Temple Place - Suite 330, ++Boston, MA 02111-1307, USA. */ ++ ++#define BITS_PER_UNIT 8 ++ ++typedef int SItype __attribute__ ((mode (SI))); ++typedef unsigned int USItype __attribute__ ((mode (SI))); ++typedef int DItype __attribute__ ((mode (DI))); ++typedef int word_type __attribute__ ((mode (__word__))); ++ ++struct DIstruct {SItype high, low;}; ++ ++typedef union ++{ ++ struct DIstruct s; ++ DItype ll; ++} DIunion; ++ ++DItype ++__ashrdi3 (DItype u, word_type b) ++{ ++ DIunion w; ++ word_type bm; ++ DIunion uu; ++ ++ if (b == 0) ++ return u; ++ ++ uu.ll = u; ++ ++ bm = (sizeof (SItype) * BITS_PER_UNIT) - b; ++ if (bm <= 0) ++ { ++ /* w.s.high = 1..1 or 0..0 */ ++ w.s.high = uu.s.high >> (sizeof (SItype) * BITS_PER_UNIT - 1); ++ w.s.low = uu.s.high >> -bm; ++ } ++ else ++ { ++ USItype carries = (USItype)uu.s.high << bm; ++ w.s.high = uu.s.high >> b; ++ w.s.low = ((USItype)uu.s.low >> b) | carries; ++ } ++ ++ return w.ll; ++} +--- /dev/null ++++ b/arch/ubicom32/lib/divmod.c +@@ -0,0 +1,85 @@ ++unsigned long ++udivmodsi4(unsigned long num, unsigned long den, int modwanted) ++{ ++ unsigned long bit = 1; ++ unsigned long res = 0; ++ ++ while (den < num && bit && !(den & (1L<<31))) ++ { ++ den <<=1; ++ bit <<=1; ++ } ++ while (bit) ++ { ++ if (num >= den) ++ { ++ num -= den; ++ res |= bit; ++ } ++ bit >>=1; ++ den >>=1; ++ } ++ if (modwanted) return num; ++ return res; ++} ++ ++long ++__udivsi3 (long a, long b) ++{ ++ return udivmodsi4 (a, b, 0); ++} ++ ++long ++__umodsi3 (long a, long b) ++{ ++ return udivmodsi4 (a, b, 1); ++} ++ ++long ++__divsi3 (long a, long b) ++{ ++ int neg = 0; ++ long res; ++ ++ if (a < 0) ++ { ++ a = -a; ++ neg = !neg; ++ } ++ ++ if (b < 0) ++ { ++ b = -b; ++ neg = !neg; ++ } ++ ++ res = udivmodsi4 (a, b, 0); ++ ++ if (neg) ++ res = -res; ++ ++ return res; ++} ++ ++long ++__modsi3 (long a, long b) ++{ ++ int neg = 0; ++ long res; ++ ++ if (a < 0) ++ { ++ a = -a; ++ neg = 1; ++ } ++ ++ if (b < 0) ++ b = -b; ++ ++ res = udivmodsi4 (a, b, 1); ++ ++ if (neg) ++ res = -res; ++ ++ return res; ++} +--- /dev/null ++++ b/arch/ubicom32/lib/lshrdi3.c +@@ -0,0 +1,62 @@ ++/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ ++/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. ++ ++This file is part of GNU CC. ++ ++GNU CC is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2, or (at your option) ++any later version. ++ ++GNU CC is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with GNU CC; see the file COPYING. If not, write to ++the Free Software Foundation, 59 Temple Place - Suite 330, ++Boston, MA 02111-1307, USA. */ ++ ++#define BITS_PER_UNIT 8 ++ ++typedef int SItype __attribute__ ((mode (SI))); ++typedef unsigned int USItype __attribute__ ((mode (SI))); ++typedef int DItype __attribute__ ((mode (DI))); ++typedef int word_type __attribute__ ((mode (__word__))); ++ ++struct DIstruct {SItype high, low;}; ++ ++typedef union ++{ ++ struct DIstruct s; ++ DItype ll; ++} DIunion; ++ ++DItype ++__lshrdi3 (DItype u, word_type b) ++{ ++ DIunion w; ++ word_type bm; ++ DIunion uu; ++ ++ if (b == 0) ++ return u; ++ ++ uu.ll = u; ++ ++ bm = (sizeof (SItype) * BITS_PER_UNIT) - b; ++ if (bm <= 0) ++ { ++ w.s.high = 0; ++ w.s.low = (USItype)uu.s.high >> -bm; ++ } ++ else ++ { ++ USItype carries = (USItype)uu.s.high << bm; ++ w.s.high = (USItype)uu.s.high >> b; ++ w.s.low = ((USItype)uu.s.low >> b) | carries; ++ } ++ ++ return w.ll; ++} +--- /dev/null ++++ b/arch/ubicom32/lib/muldi3.c +@@ -0,0 +1,87 @@ ++/* muldi3.c extracted from gcc-2.7.2.3/libgcc2.c and ++ gcc-2.7.2.3/longlong.h which is: */ ++/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. ++ ++This file is part of GNU CC. ++ ++GNU CC is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2, or (at your option) ++any later version. ++ ++GNU CC is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with GNU CC; see the file COPYING. If not, write to ++the Free Software Foundation, 59 Temple Place - Suite 330, ++Boston, MA 02111-1307, USA. */ ++ ++#define UWtype USItype ++#define UHWtype USItype ++#define W_TYPE_SIZE 32 ++#define __BITS4 (W_TYPE_SIZE / 4) ++#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) ++#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1)) ++#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2)) ++ ++#define umul_ppmm(w1, w0, u, v) \ ++ do { \ ++ UWtype __x0, __x1, __x2, __x3; \ ++ UHWtype __ul, __vl, __uh, __vh; \ ++ \ ++ __ul = __ll_lowpart (u); \ ++ __uh = __ll_highpart (u); \ ++ __vl = __ll_lowpart (v); \ ++ __vh = __ll_highpart (v); \ ++ \ ++ __x0 = (UWtype) __ul * __vl; \ ++ __x1 = (UWtype) __ul * __vh; \ ++ __x2 = (UWtype) __uh * __vl; \ ++ __x3 = (UWtype) __uh * __vh; \ ++ \ ++ __x1 += __ll_highpart (__x0);/* this can't give carry */ \ ++ __x1 += __x2; /* but this indeed can */ \ ++ if (__x1 < __x2) /* did we get it? */ \ ++ __x3 += __ll_B; /* yes, add it in the proper pos. */ \ ++ \ ++ (w1) = __x3 + __ll_highpart (__x1); \ ++ (w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0); \ ++ } while (0) ++ ++ ++#define __umulsidi3(u, v) \ ++ ({DIunion __w; \ ++ umul_ppmm (__w.s.high, __w.s.low, u, v); \ ++ __w.ll; }) ++ ++typedef int SItype __attribute__ ((mode (SI))); ++typedef unsigned int USItype __attribute__ ((mode (SI))); ++typedef int DItype __attribute__ ((mode (DI))); ++typedef int word_type __attribute__ ((mode (__word__))); ++ ++struct DIstruct {SItype high, low;}; ++ ++typedef union ++{ ++ struct DIstruct s; ++ DItype ll; ++} DIunion; ++ ++DItype ++__muldi3 (DItype u, DItype v) ++{ ++ DIunion w; ++ DIunion uu, vv; ++ ++ uu.ll = u, ++ vv.ll = v; ++ ++ w.ll = __umulsidi3 (uu.s.low, vv.s.low); ++ w.s.high += ((USItype) uu.s.low * (USItype) vv.s.high ++ + (USItype) uu.s.high * (USItype) vv.s.low); ++ ++ return w.ll; ++} |