From: Imre Kaloz Date: Mon, 22 Feb 2010 13:54:47 +0000 (+0000) Subject: move new files out from platform support patch X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=a5937732322604af25c819b05a105fc795774810;p=openwrt%2Fstaging%2F981213.git move new files out from platform support patch SVN-Revision: 19815 --- diff --git a/target/linux/ubicom32/files/arch/ubicom32/Kconfig b/target/linux/ubicom32/files/arch/ubicom32/Kconfig new file mode 100644 index 0000000000..acc6116301 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/Kconfig @@ -0,0 +1,403 @@ +# +# 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 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 + +config UBICOM32_PLIO + bool + default n + +menu "Processor type and features" + +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 OLD_40400010_SYSTEM_CALL + bool "Provide old system call interface at 0x40400010" + default y + help + Provides the old system call interface, does not affect the + new system_call interface. + +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" + +config MIN_RAMSIZE + hex "Minimum Size of RAM (in bytes)" + range 0x01000000 0x08000000 + default "0x02000000" + help + Define the minimum acceptable size of the system + RAM. Must be at least 16MB (0x01000000) + +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 + +source "arch/ubicom32/mach-common/Kconfig.switch" + +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" diff --git a/target/linux/ubicom32/files/arch/ubicom32/Kconfig.debug b/target/linux/ubicom32/files/arch/ubicom32/Kconfig.debug new file mode 100644 index 0000000000..330c758061 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/Kconfig.debug @@ -0,0 +1,129 @@ +menu "Kernel hacking" + +config TRACE_IRQFLAGS_SUPPORT + def_bool y + +config DEBUG_VERBOSE + bool "Verbose fault messages" + default y + select PRINTK + help + When a program crashes due to an exception, or the kernel detects + an internal error, the kernel can print a not so brief message + explaining what the problem was. This debugging information is + useful to developers and kernel hackers when tracking down problems, + but mostly meaningless to other people. This is always helpful for + debugging but serves no purpose on a production system. + Most people should say N here. + +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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/Makefile b/target/linux/ubicom32/files/arch/ubicom32/Makefile new file mode 100644 index 0000000000..c5712d8c88 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/Makefile @@ -0,0 +1,104 @@ +# +# arch/ubicom32/Makefile +# +# +# (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 . +# +# 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 -DIP5000 +cflags-$(CONFIG_UBICOM32_V4) := -march=ubicom32v4 -DIP7000 + +ldflags-$(CONFIG_LINKER_RELAXATION) := --relax +LDFLAGS_vmlinux := $(ldflags-y) + +GCCLIBDIR := $(dir $(shell $(CC) $(cflags-y) -print-libgcc-file-name)) +GCC_LIBS := $(GCCLIBDIR)/libgcc.a + +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/ +libs-y += $(GCC_LIBS) + +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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/Makefile b/target/linux/ubicom32/files/arch/ubicom32/crypto/Makefile new file mode 100644 index 0000000000..2d8e5863c5 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/crypto/Makefile @@ -0,0 +1,36 @@ +# +# arch/ubicom32/crypto/Makefile +# +# +# (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 . +# +# 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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/aes_ubicom32.c b/target/linux/ubicom32/files/arch/ubicom32/crypto/aes_ubicom32.c new file mode 100644 index 0000000000..f0b9f86bca --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include "crypto_ubicom32.h" +#include + +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"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/crypto_des.h b/target/linux/ubicom32/files/arch/ubicom32/crypto/crypto_des.h new file mode 100644 index 0000000000..3fc0ac3613 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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__ */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/crypto_ubicom32.c b/target/linux/ubicom32/files/arch/ubicom32/crypto/crypto_ubicom32.c new file mode 100644 index 0000000000..237ebbd9f2 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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(); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/crypto_ubicom32.h b/target/linux/ubicom32/files/arch/ubicom32/crypto/crypto_ubicom32.h new file mode 100644 index 0000000000..e754c19b90 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/des_check_key.c b/target/linux/ubicom32/files/arch/ubicom32/crypto/des_check_key.c new file mode 100644 index 0000000000..162e52477c --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * Modified by Raimar Falke 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 + * Copyright (c) Gisle Sflensminde + * Copyright (C) 2001 Niels Mvller. + * Copyright (c) 2002 James Morris + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#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"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/des_ubicom32.c b/target/linux/ubicom32/files/arch/ubicom32/crypto/des_ubicom32.c new file mode 100644 index 0000000000..14e5fb24e6 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +#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"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/md5_ubicom32.c b/target/linux/ubicom32/files/arch/ubicom32/crypto/md5_ubicom32.c new file mode 100644 index 0000000000..1f2b978ead --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +#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"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/md5_ubicom32_asm.S b/target/linux/ubicom32/files/arch/ubicom32/crypto/md5_ubicom32_asm.S new file mode 100644 index 0000000000..963a3579a6 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/crypto/md5_ubicom32_asm.S @@ -0,0 +1,234 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#define __ASM__ +#include + +#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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/sha1_ubicom32.c b/target/linux/ubicom32/files/arch/ubicom32/crypto/sha1_ubicom32.c new file mode 100644 index 0000000000..2d0c870ebf --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include + +#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"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/crypto/sha1_ubicom32_asm.S b/target/linux/ubicom32/files/arch/ubicom32/crypto/sha1_ubicom32_asm.S new file mode 100644 index 0000000000..f610b7e9f1 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#define __ASM__ +#include + +#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 +;***************************************************************************************** diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/.gitignore b/target/linux/ubicom32/files/arch/ubicom32/include/asm/.gitignore new file mode 100644 index 0000000000..c6b1010dbb --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/.gitignore @@ -0,0 +1 @@ +/ocm_size.h diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/Kbuild b/target/linux/ubicom32/files/arch/ubicom32/include/asm/Kbuild new file mode 100644 index 0000000000..c68e1680da --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/a.out.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/a.out.h new file mode 100644 index 0000000000..8eb0ade881 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h new file mode 100644 index 0000000000..78d9fcd8f2 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h @@ -0,0 +1,348 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_ATOMIC_H +#define _ASM_UBICOM32_ATOMIC_H + +#include +#include +#include + +/* + * 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. + */ + +#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 + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/audio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/audio.h new file mode 100644 index 0000000000..974d0cd63d --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/audio.h @@ -0,0 +1,40 @@ +/* + * arch/ubicom32/include/asm/audio.h + * Audio 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 . + */ + +#ifndef _AUDIO_H +#define _AUDIO_H + +#include +#include + +/* + * Resource indices used to access IRQs via platform_get_resource + */ +#define AUDIO_MEM_RESOURCE 0 +#define AUDIO_TX_IRQ_RESOURCE 0 +#define AUDIO_RX_IRQ_RESOURCE 1 + +extern struct platform_device * __init audio_device_alloc(const char *driver_name, const char *node_name, const char *inst_name, int priv_size); + +#define audio_device_priv(pdev) (((struct ubi32pcm_platform_data *)(((struct platform_device *)(pdev))->dev.platform_data))->priv_data) +#endif diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/audionode.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/audionode.h new file mode 100644 index 0000000000..f18a0e817d --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/audionode.h @@ -0,0 +1,152 @@ +/* + * audionode.h + * audionode and DMA descriptors + * + * Copyright © 2009 Ubicom Inc. . All rights reserved. + * + * This file contains confidential information of Ubicom, Inc. and your use of + * this file is subject to the Ubicom Software License Agreement distributed with + * this file. If you are uncertain whether you are an authorized user or to report + * any unauthorized use, please contact Ubicom, Inc. at +1-408-789-2200. + * Unauthorized reproduction or distribution of this file is subject to civil and + * criminal penalties. + * + */ +#ifndef _AUDIONODE_H_ +#define _AUDIONODE_H_ + +#define AUDIO_INT_FLAG_MORE_SAMPLES 0x00000001 +#define AUDIO_INT_FLAG_COMMAND 0x00000002 + +/* + * Commands the Primary OS sends to the audio device + */ +enum audio_command { + AUDIO_CMD_NONE, + AUDIO_CMD_START, + AUDIO_CMD_STOP, + AUDIO_CMD_PAUSE, + AUDIO_CMD_RESUME, + AUDIO_CMD_MUTE, + AUDIO_CMD_UNMUTE, + AUDIO_CMD_SETUP, + AUDIO_CMD_ENABLE, + AUDIO_CMD_DISABLE, +}; + +/* + * Flag bits passed in the registers + */ +#define CMD_START_FLAG_LE (1 << 0) /* Use Little Endian Mode */ + +/* + * Status bits that audio device can set to indicate reason + * for interrupting the Primary OS + */ +#define AUDIO_STATUS_PLAY_DMA0_REQUEST (1 << 0) /* Audio device needs samples in DMA0 for playback */ +#define AUDIO_STATUS_PLAY_DMA1_REQUEST (1 << 1) /* Audio device needs samples in DMA1 for playback */ + +struct 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 ipAudio 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 u32_t active; /* Nonzero if data in ptr/ctr ready to be transferred */ + volatile void *ptr; /* Pointer to data to be transferred */ + volatile u32_t ctr; /* Counter: number of data units to transfer */ +}; + +#define AUDIONODE_CAP_BE (1 << 0) +#define AUDIONODE_CAP_LE (1 << 1) + +#define AUDIONODE_VERSION 7 +struct audio_node { + struct devtree_node dn; + + /* + * Version of this node + */ + u32_t version; + + /* + * Pointer to the registers + */ + struct audio_regs *regs; +}; + +/* + * [OCM] Audio registers + * Registers exposed as part of our MMIO area + */ +#define AUDIO_REGS_VERSION 7 +struct audio_regs { + /* + * Version of this register set + */ + u32_t version; + + /* + * Interrupt status + */ + volatile u32_t int_status; + + /* + * Interrupt request + */ + volatile u32_t int_req; + + /* + * Current IRQ being serviced + */ + u32_t cur_irq; + + /* + * Maximum number of devices supported + */ + u32_t max_devs; + + /* + * [DDR] Device registers for each of the devices + */ + struct audio_dev_regs *adr; +}; + +#define AUDIO_DEV_REGS_VERSION 2 +struct audio_dev_regs { + u32_t version; /* Version of this register set */ + + u8_t name[32]; /* Name of this driver */ + u32_t caps; /* Capabilities of this driver */ + const 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; /* A bit set in a particular position means we support this channel configuration */ + volatile u32_t int_flags; /* Reason for interrupting audio device */ + volatile enum audio_command command; /* Command from Primary OS */ + volatile u32_t flags; /* Flag bits for this command */ + volatile u32_t channels; /* Number of channels */ + volatile u32_t sample_rate; /* Sample rate */ + volatile u32_t status; /* Status bits sent from ipAudio to Primary OS */ + void *primary_os_buffer_ptr; /* + * Playback: Pointer to next sample to be removed from + * Primary OS sample buffer + * Capture: Pointer to where next sample will be inserted + * into Primary OS sample buffer + */ + + /* + * These are the transfer requests. They are used in alternating + * order so that when ipAudio 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 audio_dma dma_xfer_requests[2]; +}; + +#endif diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/auxvec.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/auxvec.h new file mode 100644 index 0000000000..b2275c8578 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/bitops.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/bitops.h new file mode 100644 index 0000000000..c63837b2ed --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/bitops.h @@ -0,0 +1,172 @@ +/* + * 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 . + * + * 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 +#include /* swab32 */ + +#ifdef __KERNEL__ + +#ifndef _LINUX_BITOPS_H +#error only can be included directly +#endif + +#include +#include + +#include +#include + +#include + +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 + +/* + * 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 +#include +#include + +#include +#include +#include + +#endif /* __KERNEL__ */ + +#include +#include +#include + +#endif /* _ASM_UBICOM32_BITOPS_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/board.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/board.h new file mode 100644 index 0000000000..9df4c0f048 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/bootargs.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/bootargs.h new file mode 100644 index 0000000000..95ec393c60 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/bootinfo.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/bootinfo.h new file mode 100644 index 0000000000..79653911e4 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/bug.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/bug.h new file mode 100644 index 0000000000..b6a000daca --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/bug.h @@ -0,0 +1,95 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_BUG_H +#define _ASM_UBICOM32_BUG_H + +#include +#include + +#if defined(CONFIG_BUG) && defined(CONFIG_STOP_ON_BUG) + +/* + * BUG() + * Ubicom specific version of the BUG() macro. + * + * This implementation performs a THREAD_STALL stopping all threads before + * calling panic. This enables a developer to see the "real" state of the + * machine (since panic alters the system state). We do the printf first + * because while it is slow, it does not alter system state (like + * interrupts). + * + * TODO: Implement the trap sequence used by other architectures. + */ +#define BUG() do { \ + printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ + THREAD_STALL; \ + panic("BUG!"); \ +} while (0) + + +/* + * __WARN() + * WARN() using printk() for now. + * + * TODO: Implement the trap sequence used by other architectures. + */ +#define __WARN() \ + do { \ + printk("WARN: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ + } while(0) + +/* + * WARN_ON() + * Ubicom specific version of the WARN_ON macro. + * + * This implementation performs a printk for the WARN_ON() instead + * of faulting into the kernel and using report_bug(). + * + * TODO: Implement the trap sequence used by other architectures. + */ +#define WARN_ON(x) ({ \ + int __ret_warn_on = !!(x); \ + if (__builtin_constant_p(__ret_warn_on)) { \ + if (__ret_warn_on) \ + __WARN(); \ + } else { \ + if (unlikely(__ret_warn_on)) \ + __WARN(); \ + } \ + unlikely(__ret_warn_on); \ +}) + + +#define HAVE_ARCH_BUG +#define HAVE_ARCH_WARN_ON + +#endif + +#include + +#endif /* _ASM_UBICOM32_BUG_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/bugs.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/bugs.h new file mode 100644 index 0000000000..66adfe7b01 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/byteorder.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/byteorder.h new file mode 100644 index 0000000000..e4cd7c92f1 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/byteorder.h @@ -0,0 +1,33 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_BYTEORDER_H +#define _ASM_UBICOM32_BYTEORDER_H + +#include + +#endif /* _ASM_UBICOM32_BYTEORDER_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/cache.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/cache.h new file mode 100644 index 0000000000..5fcd36db7d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/cachectl.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/cachectl.h new file mode 100644 index 0000000000..12a9159e51 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_CACHECTL_H +#define _ASM_UBICOM32_CACHECTL_H + +#include + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/cacheflush.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/cacheflush.h new file mode 100644 index 0000000000..10a8141e5c --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/cacheflush.h @@ -0,0 +1,111 @@ +/* + * 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 . + * + * 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 + */ +#include +#include +#include + +#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. */ \ + mem_cache_control(DCCR_BASE, start, end, CCR_CTRL_FLUSH_ADDR); \ + mem_cache_control(ICCR_BASE, start, end, CCR_CTRL_INV_ADDR); \ +} while (0) + +#define flush_icache_range(start, end) \ +do { \ + /* Flush the data cache and invalidate the I cache. */ \ + mem_cache_control(DCCR_BASE, start, end, CCR_CTRL_FLUSH_ADDR); \ + mem_cache_control(ICCR_BASE, start, end, CCR_CTRL_INV_ADDR); \ +} 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) +{ + if (cc == DCCR_BASE) + UBICOM32_LOCK(DCCR_LOCK_BIT); + else + UBICOM32_LOCK(ICCR_LOCK_BIT); + + 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" + ); + + if (cc == DCCR_BASE) + UBICOM32_UNLOCK(DCCR_LOCK_BIT); + else + UBICOM32_UNLOCK(ICCR_LOCK_BIT); + +} + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/checksum.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/checksum.h new file mode 100644 index 0000000000..25f8ca61cb --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_CHECKSUM_H +#define _ASM_UBICOM32_CHECKSUM_H + +#include + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/cpu.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/cpu.h new file mode 100644 index 0000000000..c713b62538 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_CPU_H +#define _ASM_UBICOM32_CPU_H + +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/cputime.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/cputime.h new file mode 100644 index 0000000000..c794443427 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_CPUTIME_H +#define _ASM_UBICOM32_CPUTIME_H + +#include + +#endif /* _ASM_UBICOM32_CPUTIME_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/current.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/current.h new file mode 100644 index 0000000000..2d549bbf78 --- /dev/null +++ b/target/linux/ubicom32/files/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 + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_CURRENT_H +#define _ASM_UBICOM32_CURRENT_H + +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/delay.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/delay.h new file mode 100644 index 0000000000..0e5025c463 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_DELAY_H +#define _ASM_UBICOM32_DELAY_H + +#include +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/device.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/device.h new file mode 100644 index 0000000000..53de082b0d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_DEVICE_H +#define _ASM_UBICOM32_DEVICE_H + +#include + +#endif /* _ASM_UBICOM32_DEVICE_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/devtree.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/devtree.h new file mode 100644 index 0000000000..3380c2bea8 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/div64.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/div64.h new file mode 100644 index 0000000000..97702617bb --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_DIV64_H +#define _ASM_UBICOM32_DIV64_H + +#include + +#endif /* _ASM_UBICOM32_DIV64_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/dma-mapping.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/dma-mapping.h new file mode 100644 index 0000000000..87639a4292 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/dma-mapping.h @@ -0,0 +1,328 @@ +/* + * 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 . + * + * 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 +#ifdef CONFIG_PCI + +/* we implement the API below in terms of the existing PCI one, + * so include it */ +#include +/* need struct page definitions */ +#include + +static inline int +dma_supported(struct device *dev, u64 mask) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return pci_dma_supported(to_pci_dev(dev), mask); +} + +static inline int +dma_set_mask(struct device *dev, u64 dma_mask) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return pci_set_dma_mask(to_pci_dev(dev), dma_mask); +} + +static inline void * +dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, + gfp_t flag) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return pci_alloc_consistent(to_pci_dev(dev), size, dma_handle); +} + +static inline void +dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t dma_handle) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_free_consistent(to_pci_dev(dev), size, cpu_addr, dma_handle); +} + +static inline dma_addr_t +dma_map_single(struct device *dev, void *cpu_addr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return pci_map_single(to_pci_dev(dev), cpu_addr, size, (int)direction); +} + +static inline void +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_unmap_single(to_pci_dev(dev), dma_addr, size, (int)direction); +} + +static inline dma_addr_t +dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return pci_map_page(to_pci_dev(dev), page, offset, size, (int)direction); +} + +static inline void +dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_unmap_page(to_pci_dev(dev), dma_address, size, (int)direction); +} + +static inline int +dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return pci_map_sg(to_pci_dev(dev), sg, nents, (int)direction); +} + +static inline void +dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_unmap_sg(to_pci_dev(dev), sg, nhwentries, (int)direction); +} + +static inline void +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_dma_sync_single_for_cpu(to_pci_dev(dev), dma_handle, + size, (int)direction); +} + +static inline void +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_dma_sync_single_for_device(to_pci_dev(dev), dma_handle, + size, (int)direction); +} + +static inline void +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_dma_sync_sg_for_cpu(to_pci_dev(dev), sg, nelems, (int)direction); +} + +static inline void +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_dma_sync_sg_for_device(to_pci_dev(dev), sg, nelems, (int)direction); +} + +static inline int +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + return pci_dma_mapping_error(to_pci_dev(dev), dma_addr); +} + + +#else + +static inline int +dma_supported(struct device *dev, u64 mask) +{ + return 0; +} + +static inline int +dma_set_mask(struct device *dev, u64 dma_mask) +{ + BUG(); + return 0; +} + +static inline void * +dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, + gfp_t flag) +{ + BUG(); + return NULL; +} + +static inline void +dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t dma_handle) +{ + BUG(); +} + +static inline dma_addr_t +dma_map_single(struct device *dev, void *cpu_addr, size_t size, + enum dma_data_direction direction) +{ + BUG(); + return 0; +} + +static inline void +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline dma_addr_t +dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG(); + return 0; +} + +static inline void +dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline int +dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ + BUG(); + return 0; +} + +static inline void +dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline int +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + return 0; +} + +#endif + +/* Now for the API extensions over the pci_ one */ + +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) +#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) +#define dma_is_consistent(d, h) (1) + +static inline int +dma_get_cache_alignment(void) +{ + /* no easy way to get cache size on all processors, so return + * the maximum possible, to be safe */ + return (1 << INTERNODE_CACHE_SHIFT); +} + +static inline void +dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + /* just sync everything, that's all the pci API can do */ + dma_sync_single_for_cpu(dev, dma_handle, offset+size, direction); +} + +static inline void +dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + /* just sync everything, that's all the pci API can do */ + dma_sync_single_for_device(dev, dma_handle, offset+size, direction); +} + +static inline void +dma_cache_sync(struct device *dev, void *vaddr, size_t size, + enum dma_data_direction direction) +{ + /* could define this in terms of the dma_cache ... operations, + * but if you get this on a platform, you should convert the platform + * to using the generic device DMA API */ + BUG(); +} + +#endif /* _ASM_UBICOM32_DMA_MAPPING_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/dma.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/dma.h new file mode 100644 index 0000000000..c3a10acf97 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/elf.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/elf.h new file mode 100644 index 0000000000..3abc202ac6 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/elf.h @@ -0,0 +1,173 @@ +/* + * 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 . + * + * 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 +#include + +/* + * 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. + */ + +/* + * ELF_CORE_COPY_TASK_REGS is needed to dump register state from multi threaded user projects. + */ +extern int dump_task_regs(struct task_struct *, elf_gregset_t *); +#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs) + +/* 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/emergency-restart.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/emergency-restart.h new file mode 100644 index 0000000000..ea39e58ce8 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#endif /* _ASM_UBICOM32_EMERGENCY_RESTART_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/entry.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/entry.h new file mode 100644 index 0000000000..5ef46e62de --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_ENTRY_H +#define _ASM_UBICOM32_ENTRY_H + +#include +#include + +#endif /* _ASM_UBICOM32_ENTRY_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/errno.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/errno.h new file mode 100644 index 0000000000..bf995b16af --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_ERRNO_H +#define _ASM_UBICOM32_ERRNO_H + +#include + +#endif /* _ASM_UBICOM32_ERRNO_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/fb.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/fb.h new file mode 100644 index 0000000000..9e9cc6bc75 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_FB_H +#define _ASM_UBICOM32_FB_H +#include + +#define fb_pgprotect(...) do {} while (0) + +static inline int fb_is_primary_device(struct fb_info *info) +{ + return 0; +} + +#endif /* _ASM_UBICOM32_FB_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/fcntl.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/fcntl.h new file mode 100644 index 0000000000..810638c577 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#endif /* _ASM_UBICOM32_FCNTL_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/flat.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/flat.h new file mode 100644 index 0000000000..84edd478b1 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/fpu.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/fpu.h new file mode 100644 index 0000000000..496b7ddc3d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ftrace.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ftrace.h new file mode 100644 index 0000000000..40a8c178f1 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ftrace.h @@ -0,0 +1 @@ +/* empty */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/futex.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/futex.h new file mode 100644 index 0000000000..8c84b31f3b --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_FUTEX_H +#define _ASM_UBICOM32_FUTEX_H + +#include + +#endif /* _ASM_UBICOM32_FUTEX_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/gpio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/gpio.h new file mode 100644 index 0000000000..1f834f35bf --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/gpio.h @@ -0,0 +1,453 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_GPIO_H +#define _ASM_UBICOM32_GPIO_H + +#include +#include + +#include + +#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) +#define GPIO_RI_14 ((32 * 8) + 14) +#define GPIO_RI_15 ((32 * 8) + 15) + +/* + * 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 + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/hardirq.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/hardirq.h new file mode 100644 index 0000000000..e230481a13 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_HARDIRQ_H +#define _ASM_UBICOM32_HARDIRQ_H + +#include +#include + +/* + * 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 /* Standard mappings for irq_cpustat_t above */ + +extern void ack_bad_irq(unsigned int irq); + +#endif /* _ASM_UBICOM32_HARDIRQ_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/hw_irq.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/hw_irq.h new file mode 100644 index 0000000000..9dece31086 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/io.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/io.h new file mode 100644 index 0000000000..ad526a3385 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ioctl.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ioctl.h new file mode 100644 index 0000000000..10d8dd75fc --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_IOCTL_H +#define _ASM_UBICOM32_IOCTL_H + +#include + +#endif /* _ASM_UBICOM32_IOCTL_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ioctls.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ioctls.h new file mode 100644 index 0000000000..c8e2c7901f --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_IOCTLS_H +#define _ASM_UBICOM32_IOCTLS_H + +#include + +/* 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ip5000-asm.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ip5000-asm.h new file mode 100644 index 0000000000..62929e49f5 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ip5000.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ip5000.h new file mode 100644 index 0000000000..b616ebe5f0 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ip5000.h @@ -0,0 +1,845 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#ifndef _ASM_UBICOM32_IP5000_H +#define _ASM_UBICOM32_IP5000_H + +#include + +/* + * Inline assembly define + */ +#define S(arg) #arg +#define D(arg) S(arg) + +/* + * Assembler include file + */ +#include + +/* + * 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 + + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ipcbuf.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ipcbuf.h new file mode 100644 index 0000000000..76acafb20b --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/irq.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/irq.h new file mode 100644 index 0000000000..b5525894e3 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_IRQ_H +#define _ASM_UBICOM32_IRQ_H + +#include + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/irq_regs.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/irq_regs.h new file mode 100644 index 0000000000..afc33e40ee --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#endif /* _ASM_UBICOM32_IRQ_REGS_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/irqflags.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/irqflags.h new file mode 100644 index 0000000000..f40906e5a7 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/irqflags.h @@ -0,0 +1,96 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_IRQFLAGS_H +#define _ASM_UBICOM32_IRQFLAGS_H + +#include +#include +#if defined(CONFIG_SMP) +#include +#endif +#include + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/kdebug.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/kdebug.h new file mode 100644 index 0000000000..514bd27654 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_KDEBUG_H +#define _ASM_UBICOM32_KDEBUG_H + +#include + +#endif /* _ASM_UBICOM32_KDEBUG_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/kmap_types.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/kmap_types.h new file mode 100644 index 0000000000..5f4ffeacbb --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ldsr.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ldsr.h new file mode 100644 index 0000000000..b829c8790b --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_LDSR_H +#define _ASM_UBICOM32_LDSR_H + +#include +#include +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/linkage.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/linkage.h new file mode 100644 index 0000000000..63d56a2c48 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/local.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/local.h new file mode 100644 index 0000000000..9c79317309 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_LOCAL_H +#define _ASM_UBICOM32_LOCAL_H + +#include + +#endif /* _ASM_UBICOM32_LOCAL_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/machdep.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/machdep.h new file mode 100644 index 0000000000..c358154795 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_MACHDEP_H +#define _ASM_UBICOM32_MACHDEP_H + +#include + +/* 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/mc146818rtc.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/mc146818rtc.h new file mode 100644 index 0000000000..89b3c568b3 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/memory_map.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/memory_map.h new file mode 100644 index 0000000000..a1c3219b39 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/memory_map.h @@ -0,0 +1,66 @@ +/* + * arch/ubicom32/include/asm/memory_map.h + * Machine memory maps/ + * + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_MEMORY_MAP_H +#define _ASM_UBICOM32_MEMORY_MAP_H + +/* + * 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) + +#define SDRAMSTART 0x40000000 + +#define KERNELSTART (SDRAMSTART + 0x00400000) + +#define FLASHSTART 0x60000000 + +/* + * CODELOADER / OS_SYSCALL OCM Reservations + * Don't change these unless you know what you are doing. + */ +#define CODELOADER_SIZE 0x30 +#define CODELOADER_BEGIN OCMSTART /* Must be OCM start for gdb to work. */ +#define CODELOADER_END (CODELOADER_BEGIN + CODELOADER_SIZE) + +#define OS_SYSCALL_BEGIN CODELOADER_END /* system_call at this address */ +#define OS_SYSCALL_SIZE (512 - CODELOADER_SIZE) +#define OS_SYSCALL_END (OS_SYSCALL_BEGIN + OS_SYSCALL_SIZE) + +#endif /* _ASM_UBICOM32_MEMORY_MAP_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/mman.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/mman.h new file mode 100644 index 0000000000..a2a3efe056 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_MMAN_H +#define _ASM_UBICOM32_MMAN_H + +#include + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/mmu.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/mmu.h new file mode 100644 index 0000000000..71b604b529 --- /dev/null +++ b/target/linux/ubicom32/files/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 + * + * 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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/mmu_context.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/mmu_context.h new file mode 100644 index 0000000000..0123a6c91e --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/module.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/module.h new file mode 100644 index 0000000000..1c891c674f --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/module.h @@ -0,0 +1,48 @@ +/* + * 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 . + * + * 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) + +#define ARCH_OOPS_MODULE_EXTRA(mod) \ + printk(KERN_INFO "%p %u OCM(%p %u)\n", \ + (mod)->module_core, (mod)->core_size, \ + (mod)->arch.ocm_inst, (mod)->arch.ocm_inst_size) +#endif /* _ASM_UBICOM32_MODULE_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/msgbuf.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/msgbuf.h new file mode 100644 index 0000000000..8d575e0f9d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/mutex.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/mutex.h new file mode 100644 index 0000000000..5ab4de0dac --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#endif /* _ASM_UBICOM32_MUTEX_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/namei.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/namei.h new file mode 100644 index 0000000000..8010c148f2 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ocm-alloc.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ocm-alloc.h new file mode 100644 index 0000000000..ee29d84107 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ocm_size.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ocm_size.h new file mode 100644 index 0000000000..55778fe196 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ocm_size.h @@ -0,0 +1,3 @@ +#define APP_OCM_CODE_SIZE (0x3ffc2e00-0x3ffc0000) +#define APP_OCM_DATA_SIZE (0x3ffd3500-0x3ffc8000) + diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ocm_text.lds.inc b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ocm_text.lds.inc new file mode 100644 index 0000000000..5456cab558 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ocm_text.lds.inc @@ -0,0 +1,175 @@ +/* + * arch/ubicom32/include/asm/ocm_text.lds.inc + * + * + * (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 . + * + * 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) diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/page.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/page.h new file mode 100644 index 0000000000..89c6ce61c7 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#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 + +#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 +#endif + +#endif /* _ASM_UBICOM32_PAGE_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/page_offset.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/page_offset.h new file mode 100644 index 0000000000..8536568a99 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/param.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/param.h new file mode 100644 index 0000000000..00e5a79034 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/pci.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/pci.h new file mode 100644 index 0000000000..0dce8bdc60 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_PCI_H +#define _ASM_UBICOM32_PCI_H + +#include + +/* 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/pcm_tio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/pcm_tio.h new file mode 100644 index 0000000000..13b5fc1e88 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/pcm_tio.h @@ -0,0 +1,84 @@ +/* + * arch/ubicom32/include/asm/pcm_tio.h + * Ubicom32 architecture PCM 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 . + */ +#ifndef _ASM_UBICOM32_PCM_TIO_H +#define _ASM_UBICOM32_PCM_TIO_H + +#include + +#define PCM_TIO_REGS_VERSION 2 +struct pcm_tio_regs { + /* + * set this value to 1 to reload the parameters and restart the HRT + */ + u32_t reload; + + /* + * Pointers to the input and output buffers + */ + void *input_buf; + void *output_buf; + + /* + * Buffer size (see pcm_hrt.S for constraints) + */ + u32_t buffer_size; + + /* + * Current cycle. This variable increases every time half the buffer + * is consumed. + */ + u32_t cycle; + + /* + * Fields below this line are not accessed by the HRT. They are purely + * informational for the user of this TIO. + */ + + /* + * Version of this structure + */ + u32_t version; + + /* + * Number of channels supported + */ + u32_t channels; + + /* + * Maximum buffer size + */ + u32_t max_buffer_size; +}; + +/* + * Our device node + */ +#define PCM_TIO_NODE_VERSION 1 +struct pcm_tio_node { + struct devtree_node dn; + u32_t version; + struct pcm_tio_regs *regs; +}; + +#endif + diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/percpu.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/percpu.h new file mode 100644 index 0000000000..4d51e60559 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_PERCPU_H +#define _ASM_UBICOM32_PERCPU_H + +#include + +#endif /* _ASM_UBICOM32_PERCPU_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/pgalloc.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/pgalloc.h new file mode 100644 index 0000000000..397d19e419 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_PGALLOC_H +#define _ASM_UBICOM32_PGALLOC_H + +#include +#include + +#define check_pgt_cache() do { } while (0) + +#endif /* _ASM_UBICOM32_PGALLOC_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/pgtable.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/pgtable.h new file mode 100644 index 0000000000..70ad115ccf --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +//vic - this bit copied from m68knommu version +#include +#include +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/plio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/plio.h new file mode 100644 index 0000000000..ade78f246b --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/plio.h @@ -0,0 +1,313 @@ +/* + * plio.h + * PLIO defines. + * + * Copyright © 2009 Ubicom Inc. . 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 . + * This file contains confidential information of Ubicom, Inc. and your use of + * this file is subject to the Ubicom Software License Agreement distributed with + * this file. If you are uncertain whether you are an authorized user or to report + * any unauthorized use, please contact Ubicom, Inc. at +1-408-789-2200. + * Unauthorized reproduction or distribution of this file is subject to civil and + * criminal penalties. + */ + +#ifndef __PLIO__H__ +#define __PLIO__H__ + +#include +#include + +#define PLIO_PORT RD +#define PLIO_EXT_PORT RI + +#define TRANSMIT_FIFO_WATERMARK 8 + +/* + * PLIO non-blocking register definitions + */ +#define PLIO_FN 2 + +typedef struct { + unsigned : 10; + unsigned rxfifo_thread_enable: 1; /* allowed rxfifo thread enable */ + unsigned : 1; + unsigned rxfifo_thread: 4; /* allowed rxfifo thread access */ + unsigned : 4; + unsigned br_thread: 4; /* allowed blocking region thread access */ + unsigned fn_reset: 4; /* function reset bit vector */ + unsigned rxfifo_sel: 1; /* select between RXFIFO 0 and 1 */ + unsigned fn_sel: 3; /* select port function */ +} plio_io_function_t; + +typedef struct { + unsigned : 24; + unsigned pin:8; +} plio_gpio_t; + +typedef struct { + unsigned : 16; + unsigned txfifo_uf: 1; /* TXFIFO underflow */ + unsigned txfifo_wm: 1; /* TXFIFO watermark */ + unsigned rxfifo_of: 1; /* RXFIFO overflow */ + unsigned rxfifo_wm: 1; /* RXFIFO watermark */ + unsigned : 5; + unsigned lreg_int_addr_rd: 1; /* read from specified LREG address */ + unsigned lreg_int_addr_wr: 1; /* write to specified LREG address */ + unsigned extctl_int: 4; /* synchronized external interrupts */ + unsigned pfsm_int: 1; /* state machine */ +} plio_intstat_t; + +typedef struct { + unsigned txfifo_reset: 1; /* TXFIFO reset for int_set only */ + unsigned rxfifo_reset: 1; /* RXFIFO reset for int_set only */ + unsigned : 11; + unsigned idif_txfifo_flush: 1; /* flush TXFIFO and idif_txfifo */ + unsigned idif_rxfifo_flush: 1; /* flush RXFIFO and idif_rxfifo */ + unsigned pfsm_start: 1; /* input to fsm */ + unsigned txfifo_uf: 1; /* TXFIFO underflow */ + unsigned txfifo_wm: 1; /* TXFIFO watermark */ + unsigned rxfifo_of: 1; /* RXFIFO overflow */ + unsigned rxfifo_wm: 1; /* RXFIFO watermark */ + unsigned : 5; + unsigned lreg_int_addr_rd: 1; /* read from specified LREG address */ + unsigned lreg_int_addr_wr: 1; /* write to specified LREG address */ + unsigned extctl_int: 4; /* synchronized external interrupts */ + unsigned pfsm_int: 1; /* state machine */ +} plio_intset_t; + +typedef enum { + PLIO_PORT_MODE_D, + PLIO_PORT_MODE_DE, + PLIO_PORT_MODE_DI, + PLIO_PORT_MODE_DEI, + PLIO_PORT_MODE_DC, +} plio_port_mode_t; + +typedef enum { + PLIO_CLK_CORE, /* CORE CLK */ + PLIO_CLK_IO, /* IO CLK */ + PLIO_CLK_EXT, /* EXT CLK */ +} plio_clk_src_t; +typedef struct { + unsigned : 4; + unsigned edif_iaena_sel: 1; /* Input Address Enable Select */ + unsigned edif_iaclk_sel: 1; /* Input Address Clock Select */ + unsigned edif_iald_inv: 1; /* Input Address Strobe Invert */ + unsigned edif_idclk_sel: 1; /* Input Data Clock Select */ + unsigned edif_idld_inv: 1; /* Input Data Strobe Invert */ + unsigned edif_ds: 3; /* specify IDR and ODR data shift */ + unsigned edif_cmp_mode: 1; /* configure IDR comparator output */ + unsigned edif_idena_sel: 1; /* Input Data Enable Select */ + unsigned ecif_extclk_ena: 1; /* plio_extctl output select */ + unsigned idif_tx_fifo_cmd_sel: 1; /* select pfsm_cmd data word position */ + unsigned ptif_porti_cfg: 2; /* select port I pin configuration */ + unsigned ptif_portd_cfg: 3; /* select port D pin configuration */ + plio_port_mode_t ptif_port_mode: 3; /* select other plio ports */ + unsigned icif_clk_plio_ext_inv: 1; /* invert external plio clock when set */ + unsigned icif_rst_plio: 1; /* reset plio function and io fifos */ + plio_clk_src_t icif_clk_src_sel: 2; /* select plio clock source */ + unsigned pfsm_prog: 1; /* enable pfsm programming */ + unsigned pfsm_cmd: 3; /* software input to pfsm */ +} plio_fctl0_t; + +typedef struct { + unsigned : 2; + unsigned idif_byteswap_tx: 3; /* swap TXFIFO byte order */ + unsigned idif_byteswap_rx: 3; /* swap RXFIFO byte order */ + unsigned : 1; + unsigned lreg_ena: 1; /* enable local register map */ + unsigned lreg_addr_fifo_cmp_ena: 1; /* enable a specific LREG address from/to TX/RX fifos */ + unsigned lreg_addr_fifo_cmp: 5; /* LREG address routed from/to TX/RX fifos */ + unsigned : 1; + unsigned dcod_iald_idld_sel: 2; /* select address/data strobes */ + unsigned dcod_rw_src_sel: 1; /* select LREG strobe source */ + unsigned dcod_rd_sel: 5; /* select read strobe source */ + unsigned dcod_wr_sel: 5; /* select write strobe source */ + unsigned dcod_rd_lvl: 1; /* select active level of read strobe */ + unsigned dcod_wr_lvl: 1; /* select active level of read strobe */ +} plio_fctl1_t; + +typedef struct { + unsigned icif_eclk_div: 16; /* external plio clock divider */ + unsigned icif_iclk_div: 16; /* internal plio clock divider */ +} plio_fctl2_t; + +typedef struct { + unsigned : 27; + unsigned pfsm_state: 5; /* current pfsm state */ +} plio_stat_0_t; + +typedef struct { + unsigned : 3; + unsigned lreg_r_int_addr: 5; + unsigned : 11; + unsigned lreg_w_int_addr: 5; + unsigned lreg_w_int_data: 8; +} plio_stat_1_t; + +typedef struct { + unsigned : 32; +} plio_stat_2_t; + +typedef struct { + unsigned tx: 16; + unsigned rx: 16; +} plio_io_fifo_wm_t, plio_io_fifo_lvl_t; + + +/* plio blocking region register definitions + */ +typedef struct { + unsigned ns1: 5; + unsigned ic1: 7; + unsigned ec1: 4; + unsigned ns0: 5; + unsigned ic0: 7; + unsigned ec0: 4; +} plio_sram_t; + +typedef struct { + unsigned : 2; + unsigned s9: 3; + unsigned s8: 3; + unsigned s7: 3; + unsigned s6: 3; + unsigned s5: 3; + unsigned s4: 3; + unsigned s3: 3; + unsigned s2: 3; + unsigned s1: 3; + unsigned s0: 3; +} plio_grpsel_t; + +typedef struct { + unsigned s7: 4; + unsigned s6: 4; + unsigned s5: 4; + unsigned s4: 4; + unsigned s3: 4; + unsigned s2: 4; + unsigned s1: 4; + unsigned s0: 4; +} plio_cs_lut_t; + +typedef struct { + unsigned lut3: 8; + unsigned lut2: 8; + unsigned lut1: 8; + unsigned lut0: 8; +} plio_extctl_t; + +typedef struct { + plio_grpsel_t grpsel[4]; + u16_t cv[16]; + plio_cs_lut_t cs_lut[4]; + plio_extctl_t extctl_o_lut[8]; +} plio_pfsm_t; + +typedef struct { + u32_t odr_oe_sel; + u32_t odr_oe; + u32_t cmp; + u32_t ncmp; + u32_t cmp_mask; +} plio_edif_t; + +typedef enum { + PLIO_ECIF_CLK_OUT = 9, + PLIO_ECIF_IALD = 9, + PLIO_ECIF_CLK_IN = 8, + PLIO_ECIF_IDLD = 8, + PLIO_ECIF_INT = 2, +} plio_ecif_output_t; + +typedef struct { + u32_t bypass_sync; + u32_t ift; + u32_t output_type; + u32_t output_ena; + u32_t output_lvl; +} plio_ecif_t; + +typedef struct { + u32_t idr_addr_pos_mask; + u32_t reserved; + u32_t lreg_bar; +} plio_dcod_t; + +typedef struct { + u32_t addr_rd_ena; + u32_t addr_wr_ena; + u32_t addr_rd_int_ena; + u32_t addr_wr_int_ena; +} plio_lcfg_t; + + +/* + * PLIO configuration + */ +typedef struct { + plio_fctl0_t fctl0; + plio_fctl1_t fctl1; + plio_fctl2_t fctl2; +} plio_fctl_t; + +typedef struct { + plio_pfsm_t pfsm; + plio_edif_t edif; + plio_ecif_t ecif; + plio_dcod_t dcod; + plio_lcfg_t lcfg; +} plio_config_t; + +typedef struct { + plio_io_function_t function; + plio_gpio_t gpio_ctl; + plio_gpio_t gpio_out; + plio_gpio_t gpio_in; + plio_intstat_t intstat; + plio_intstat_t intmask; + plio_intset_t intset; + plio_intstat_t intclr; + unsigned tx_lo; + unsigned tx_hi; + unsigned rx_lo; + unsigned rx_hi; + plio_fctl0_t fctl0; + plio_fctl1_t fctl1; + plio_fctl2_t fctl2; + plio_stat_0_t stat0; + plio_stat_1_t stat1; + plio_stat_2_t stat2; + plio_io_fifo_wm_t fifo_wm; + plio_io_fifo_lvl_t fifo_lvl; +} plio_nbr_t; + +typedef struct { + u32_t pfsm_sram[256]; + plio_config_t config; +} plio_br_t; + +#define PLIO_NBR ((plio_nbr_t *)(PLIO_PORT)) +#define PLIO_BR ((plio_br_t *)((PLIO_PORT + IO_PORT_BR_OFFSET))) +#define PEXT_NBR ((plio_nbr_t *)(PLIO_EXT_PORT)) + +extern void plio_init(const plio_fctl_t *plio_fctl, const plio_config_t *plio_config, const plio_sram_t plio_sram_cfg[], int sram_cfg_size); + +#endif // __PLIO__H__ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/poll.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/poll.h new file mode 100644 index 0000000000..8e92b76376 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#endif /* _ASM_UBICOM32_POLL_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/posix_types.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/posix_types.h new file mode 100644 index 0000000000..40b69f8518 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/processor.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/processor.h new file mode 100644 index 0000000000..92e49e81ac --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 void processor_ocm(unsigned long *socm, unsigned long *eocm); +extern void processor_dram(unsigned long *sdram, unsigned long *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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/profilesample.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/profilesample.h new file mode 100644 index 0000000000..6e42269e3b --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/profilesample.h @@ -0,0 +1,44 @@ +/* + * arch/ubicom32/mach-common/profile.h + * Private data for the profile module + * + * (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 . + * + */ + + +#ifndef _PROFILESAMPLE_H_ +#define _PROFILESAMPLE_H_ + +/* + * a sample taken by the ipProfile package for sending to the profilertool + */ +struct profile_sample { + unsigned int pc; /* PC value */ + unsigned int a5; /* a5 contents for parent of leaf function */ + unsigned int parent; /* return address from stack, to find the caller */ + unsigned int latency; /* CPU clocks since the last message dispatch in this thread (thread 0 ony for now) */ + unsigned short active; /* which threads are active - for accurate counting */ + unsigned short d_blocked; /* which threads are blocked due to D cache misses */ + unsigned short i_blocked; /* which threads are blocked due to I cache misses */ + unsigned char cond_codes; /* for branch prediction */ + unsigned char thread; /* I-blocked, D-blocked, 4-bit thread number */ +}; + +#endif diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ptrace.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ptrace.h new file mode 100644 index 0000000000..aeae2c78ce --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/range-protect-asm.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/range-protect-asm.h new file mode 100644 index 0000000000..87459e4e9d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/range-protect.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/range-protect.h new file mode 100644 index 0000000000..b4bd2f043c --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +/* + * 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 + +#endif +#endif /* _ASM_UBICOM32_RANGE_PROTECT_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/resource.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/resource.h new file mode 100644 index 0000000000..0a59a4f257 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_RESOURCE_H +#define _ASM_UBICOM32_RESOURCE_H + +#include + +#endif /* _ASM_UBICOM32_RESOURCE_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ring_tio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ring_tio.h new file mode 100644 index 0000000000..c02a445208 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + */ +#ifndef _ASM_UBICOM32_RING_TIO_H +#define _ASM_UBICOM32_RING_TIO_H + +#include + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/scatterlist.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/scatterlist.h new file mode 100644 index 0000000000..4fb4df1717 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SCATTERLIST_H +#define _ASM_UBICOM32_SCATTERLIST_H + +#include +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/sd_tio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/sd_tio.h new file mode 100644 index 0000000000..ecb2b8272d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + */ +#ifndef _ASM_UBICOM32_SD_TIO_H +#define _ASM_UBICOM32_SD_TIO_H + +#include + +/* + * Devtree node for SD + */ +struct sd_tio_node { + struct devtree_node dn; + void *regs; +}; + +#endif /* _ASM_UBICOM32_SD_TIO_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/sections.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/sections.h new file mode 100644 index 0000000000..eb7d24bc21 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SECTIONS_H +#define _ASM_UBICOM32_SECTIONS_H + +#include + +#endif /* _ASM_UBICOM32_SECTIONS_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/segment.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/segment.h new file mode 100644 index 0000000000..3f10490767 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/semaphore-helper.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/semaphore-helper.h new file mode 100644 index 0000000000..ad0aaf0a28 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/semaphore.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/semaphore.h new file mode 100644 index 0000000000..d20ead3103 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include +#include + +#include +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/sembuf.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/sembuf.h new file mode 100644 index 0000000000..996a7a011e --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/setup.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/setup.h new file mode 100644 index 0000000000..ad8dc57608 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/shmbuf.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/shmbuf.h new file mode 100644 index 0000000000..7cc4339666 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/shmparam.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/shmparam.h new file mode 100644 index 0000000000..851d062708 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/sigcontext.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/sigcontext.h new file mode 100644 index 0000000000..8bbeb82a70 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SIGCONTEXT_H +#define _ASM_UBICOM32_SIGCONTEXT_H + +#include + +struct sigcontext { + struct pt_regs sc_regs; +}; + +#endif /* _ASM_UBICOM32_SIGCONTEXT_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/siginfo.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/siginfo.h new file mode 100644 index 0000000000..be0a4653c3 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SIGINFO_H +#define _ASM_UBICOM32_SIGINFO_H + +#include + +#endif /* _ASM_UBICOM32_SIGINFO_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/signal.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/signal.h new file mode 100644 index 0000000000..a334e19ea3 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SIGNAL_H +#define _ASM_UBICOM32_SIGNAL_H + +#include + +/* 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 + +#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 +#undef __HAVE_ARCH_SIG_BITOPS + +#define ptrace_signal_deliver(regs, cookie) do { } while (0) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_UBICOM32_SIGNAL_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/smp.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/smp.h new file mode 100644 index 0000000000..651a53695e --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/smp.h @@ -0,0 +1,87 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SMP_H +#define _ASM_UBICOM32_SMP_H + +#ifndef CONFIG_SMP +#error you should not include smp.h if smp is off +#endif + +#ifndef ASSEMBLY +#include +#include +#include +#include + +typedef unsigned long address_t; +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(const struct cpumask *mask); +extern void smp_set_affinity(unsigned int irq, const struct cpumask *dest); +extern void arch_send_call_function_single_ipi(int cpu); +#define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask +extern void arch_send_call_function_ipi_mask(const struct cpumask *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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/socket.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/socket.h new file mode 100644 index 0000000000..2d95c24f78 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/socket.h @@ -0,0 +1,87 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SOCKET_H +#define _ASM_UBICOM32_SOCKET_H + +#include + +/* 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 + +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + +#endif /* _ASM_UBICOM32_SOCKET_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/sockios.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/sockios.h new file mode 100644 index 0000000000..5a7708d96d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/spinlock.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/spinlock.h new file mode 100644 index 0000000000..40080584c0 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/spinlock.h @@ -0,0 +1,296 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SPINLOCK_H +#define _ASM_UBICOM32_SPINLOCK_H + +#include +#include +#include + +/* + * __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_read_lock_flags(lock, flags) __raw_read_lock(lock) +#define __raw_write_lock_flags(lock, flags) __raw_write_lock(lock) + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/spinlock_types.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/spinlock_types.h new file mode 100644 index 0000000000..3745bca63f --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/stacktrace.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/stacktrace.h new file mode 100644 index 0000000000..f278ac7b5c --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/stat.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/stat.h new file mode 100644 index 0000000000..e333c60e61 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/statfs.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/statfs.h new file mode 100644 index 0000000000..15890d436d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_STATFS_H +#define _ASM_UBICOM32_STATFS_H + +#include + +#endif /* _ASM_UBICOM32_STATFS_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/string.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/string.h new file mode 100644 index 0000000000..7f24bf593b --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/string.h @@ -0,0 +1,40 @@ +/* + * 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 . + * + * 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); + +#define __HAVE_ARCH_MEMMOVE +extern void * memmove(void *to, const void *from, size_t len); + +#endif /* _ASM_UBICOM32_STRING_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/swab.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/swab.h new file mode 100644 index 0000000000..180a50311a --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/swab.h @@ -0,0 +1,45 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_BYTEORDER_H +#define _ASM_UBICOM32_BYTEORDER_H + +#include + +#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 */ + +#endif /* _ASM_UBICOM32_BYTEORDER_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/switch-dev.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/switch-dev.h new file mode 100644 index 0000000000..ed67e94aad --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/switch-dev.h @@ -0,0 +1,51 @@ +/* + * arch/ubicom32/include/asm/switch-dev.h + * generic Ethernet switch 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SWITCH_DEV_H +#define _ASM_UBICOM32_SWITCH_DEV_H + +#define SWITCH_DEV_FLAG_HW_RESET 0x01 +#define SWITCH_DEV_FLAG_SW_RESET 0x02 + +struct switch_core_platform_data { + /* + * See flags above + */ + u32_t flags; + + /* + * GPIO to use for nReset + */ + int pin_reset; + + /* + * Name of this switch + */ + const char *name; +}; + +#endif /* _ASM_UBICOM32_SWITCH_DEV_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/system.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/system.h new file mode 100644 index 0000000000..c3ec6083e9 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_SYSTEM_H +#define _ASM_UBICOM32_SYSTEM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/termbits.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/termbits.h new file mode 100644 index 0000000000..a7b3381f86 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_TERMBITS_H +#define _ASM_UBICOM32_TERMBITS_H + +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/termios.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/termios.h new file mode 100644 index 0000000000..5c16fe7e9b --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_TERMIOS_H +#define _ASM_UBICOM32_TERMIOS_H + +#include +#include + +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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/thread-asm.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/thread-asm.h new file mode 100644 index 0000000000..f9566037e5 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/thread-asm.h @@ -0,0 +1,51 @@ +/* + * 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 . + * + * 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 + * + * Note that we don't need to mask the result as bits 6 through 31 of the + * ROSR are zeroes. + */ +.macro thread_get_self reg + lsr.4 \reg, ROSR, #2 +.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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/thread.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/thread.h new file mode 100644 index 0000000000..69c925ea47 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/thread.h @@ -0,0 +1,320 @@ +/* + * 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 . + * + * 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 +#include + +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; + + /* + * Note that ROSR has zeroes in bits 6 through 31 and so we don't need + * to do any additional bit masking here. + */ + asm ( + "lsr.4 %0, ROSR, #2 \n\t" + : "=d" (result) + : + : "cc" + ); + + return result; +} + +/* + * 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 + * + * Note that we don't need to mask the result as bits 6 through 31 of the + * ROSR are zeroes. + */ +".macro thread_get_self reg \n\t" +" lsr.4 \\reg, ROSR, #2 \n\t" +".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 + +#endif /* __ASSEMBLY__ */ +#endif /* _ASM_UBICOM32_THREAD_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/thread_info.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/thread_info.h new file mode 100644 index 0000000000..55c69dadd7 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +/* + * 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< 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<. + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/tlb.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/tlb.h new file mode 100644 index 0000000000..cd45996d57 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#endif /* _ASM_UBICOM32_TLB_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/tlbflush.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/tlbflush.h new file mode 100644 index 0000000000..0317aca8d0 --- /dev/null +++ b/target/linux/ubicom32/files/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 + * Copyright (C) 2000-2002, Greg Ungerer + * + * 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 . + * + * 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 + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/topology.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/topology.h new file mode 100644 index 0000000000..b4de1437dc --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_TOPOLOGY_H +#define _ASM_UBICOM32_TOPOLOGY_H + +#include + +#endif /* _ASM_UBICOM32_TOPOLOGY_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/traps.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/traps.h new file mode 100644 index 0000000000..9edcca9b5b --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/types.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/types.h new file mode 100644 index 0000000000..5b960dda3b --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/uaccess.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/uaccess.h new file mode 100644 index 0000000000..eef739d129 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include + +#include + +#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 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/uart_tio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/uart_tio.h new file mode 100644 index 0000000000..19ef82efa6 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/uart_tio.h @@ -0,0 +1,126 @@ +/* + * arch/ubicom32/include/asm/uart_tio.h + * Ubicom32 architecture UART 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 . + */ +#ifndef _ASM_UBICOM32_UART_TIO_H +#define _ASM_UBICOM32_UART_TIO_H + +#include + +#define UARTTIO_RX_FIFO_SIZE 16 +#define UARTTIO_TX_FIFO_SIZE 16 + +/* + * Interrupt flags + */ +#define UARTTIO_UART_INT_RX 0x00000001 // set when a character has been recevied (TODO: add watermark) +#define UARTTIO_UART_INT_RXOVF 0x00000002 // set when the receive buffer has overflowed +#define UARTTIO_UART_INT_RXFRAME 0x00000004 // set when there has been a framing error + +#define UARTTIO_UART_INT_TX 0x00000100 // set every time a character is transmitted +#define UARTTIO_UART_INT_TXBE 0x00000200 // set when the transmit buffer is empty (TODO: add watermark) + +#define UARTTIO_UART_FLAG_ENABLED 0x80000000 +#define UARTTIO_UART_FLAG_SET_RATE 0x00000001 // set to update baud rate +#define UARTTIO_UART_FLAG_RESET 0x00000002 // set to reset the port +struct uarttio_uart { + volatile u32_t flags; + + volatile u32_t baud_rate; + volatile u32_t current_baud_rate; + u32_t bit_time; + + /* + * Modem status register + */ + volatile u32_t status; + + /* + * Interrupt registers + */ + volatile u32_t int_mask; + volatile u32_t int_flags; + + /* + * Ports and pins + */ + u32_t rx_port; + u32_t tx_port; + + u8_t rx_pin; + u8_t tx_pin; + + /* + * Configuration Data + */ + u8_t rx_bits; + u8_t rx_stop_bits; + u8_t tx_bits; + u8_t tx_stop_bits; + + /* + * RX state machine data + */ + u32_t rx_timer; + u32_t rx_bit_pos; + u32_t rx_byte; + u32_t rx_fifo_head; + u32_t rx_fifo_tail; + u32_t rx_fifo_size; + + /* + * TX state machine data + */ + u32_t tx_timer; + u32_t tx_bit_pos; + u32_t tx_byte; + u32_t tx_fifo_head; + u32_t tx_fifo_tail; + u32_t tx_fifo_size; + + /* + * FIFOs + */ + u8_t rx_fifo[UARTTIO_RX_FIFO_SIZE]; + u8_t tx_fifo[UARTTIO_TX_FIFO_SIZE]; +}; + +#define UARTTIO_VP_VERSION 1 +struct uarttio_regs { + u32_t version; + + u32_t thread; + + u32_t max_uarts; + + struct uarttio_uart uarts[0]; +}; + +#define UARTTIO_NODE_VERSION 1 +struct uarttio_node { + struct devtree_node dn; + + u32_t version; + struct uarttio_regs *regs; + u32_t regs_sz; +}; + +#endif /* _ASM_UBICOM32_UART_TIO_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubi32-cs4384.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubi32-cs4384.h new file mode 100644 index 0000000000..18e7634ff7 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubi32-cs4384.h @@ -0,0 +1,52 @@ +/* + * 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 . + */ +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubi32-pcm.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubi32-pcm.h new file mode 100644 index 0000000000..ab14b36816 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubi32-pcm.h @@ -0,0 +1,54 @@ +/* + * 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 . + */ +#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 audio node/inst + */ + const char *node_name; + const char *inst_name; + int inst_num; + + /* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-common-asm.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-common-asm.h new file mode 100644 index 0000000000..82696bbbfc --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-common.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-common.h new file mode 100644 index 0000000000..1f05a8cd39 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-common.h @@ -0,0 +1,128 @@ +/* + * 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 . + * + * 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. + * - DCCR_LOCK_BIT - Used to limit access to the DCCR cache control peripheral + * - ICCR_LOCK_BIT - Used to limit access to the ICCR cache control peripheral + * - 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 +#define ICCR_LOCK_BIT 27 +#define DCCR_LOCK_BIT 26 + +#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 + +#endif /* __ASSEMBLY__ */ +#endif /* _ASM_UBICOM32_UBICOM32_COMMON_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-spi-gpio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-spi-gpio.h new file mode 100644 index 0000000000..b5379e3eff --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-tio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32-tio.h new file mode 100644 index 0000000000..4d87e5c0ee --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32bl.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32bl.h new file mode 100644 index 0000000000..498c754964 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32fb.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32fb.h new file mode 100644 index 0000000000..ae994e261e --- /dev/null +++ b/target/linux/ubicom32/files/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 . + */ +#ifndef _ASM_UBICOM32_UBICOM32FB_H +#define _ASM_UBICOM32_UBICOM32FB_H + +#include + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32hid.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32hid.h new file mode 100644 index 0000000000..d324313f10 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32input.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32input.h new file mode 100644 index 0000000000..dea5c79286 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32input_i2c.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32input_i2c.h new file mode 100644 index 0000000000..eb16723750 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32lcd.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32lcd.h new file mode 100644 index 0000000000..eea5340fd4 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32lcd.h @@ -0,0 +1,38 @@ +/* + * 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 . + */ +#ifndef _ASM_UBICOM32_UBICOM32_LCD_H +#define _ASM_UBICOM32_UBICOM32_LCD_H + +#include + +struct ubicom32lcd_platform_data { + int pin_cs; + int pin_rs; + int pin_rd; + int pin_wr; + int pin_reset; + int data_shift; + struct ubicom32_io_port *port_data; +}; + +#endif /* _ASM_UBICOM32_UBICOM32_LCD_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32lcdpower.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32lcdpower.h new file mode 100644 index 0000000000..03b8a7eaaa --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32lcdpower.h @@ -0,0 +1,39 @@ +/* + * arch/ubicom32/include/asm/ubicom32lcdpower.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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_UBICOM32_LCDPOWER_H +#define _ASM_UBICOM32_UBICOM32_LCDPOWER_H + +struct ubicom32lcdpower_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_LCDPOWER_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32ring.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32ring.h new file mode 100644 index 0000000000..dd9c8f76c5 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + */ + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32sd.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32sd.h new file mode 100644 index 0000000000..b5cebfa2ed --- /dev/null +++ b/target/linux/ubicom32/files/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 . + */ +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32suart.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32suart.h new file mode 100644 index 0000000000..824d0de2e4 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ubicom32suart.h @@ -0,0 +1,36 @@ +/* + * arch/ubicom32/include/asm/ubicom32suart.h + * + * + * (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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/ucontext.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/ucontext.h new file mode 100644 index 0000000000..71c1129023 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/unaligned.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/unaligned.h new file mode 100644 index 0000000000..41b2646604 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#include +#include +#include + +#define get_unaligned __get_unaligned_be +#define put_unaligned __put_unaligned_be + +#endif /* _ASM_UBICOM32_UNALIGNED_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/unistd.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/unistd.h new file mode 100644 index 0000000000..2c7ba56006 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/user.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/user.h new file mode 100644 index 0000000000..2e79786ca9 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_USER_H +#define _ASM_UBICOM32_USER_H + +#include +#include +/* + * Adapted from + * + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/vdc_tio.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/vdc_tio.h new file mode 100644 index 0000000000..cc45fc552c --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/vga.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/vga.h new file mode 100644 index 0000000000..793435f8b1 --- /dev/null +++ b/target/linux/ubicom32/files/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 + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#ifndef _ASM_UBICOM32_VGA_H +#define _ASM_UBICOM32_VGA_H + +#include + +/* + * 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 + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/xor.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/xor.h new file mode 100644 index 0000000000..31edcceb62 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_XOR_H +#define _ASM_UBICOM32_XOR_H + +#include + +#endif /* _ASM_UBICOM32_XOR_H */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/Makefile b/target/linux/ubicom32/files/arch/ubicom32/kernel/Makefile new file mode 100644 index 0000000000..6294fa2eaa --- /dev/null +++ b/target/linux/ubicom32/files/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 . +# +# 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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/asm-offsets.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/asm-offsets.c new file mode 100644 index 0000000000..639a536a18 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/asm-offsets.c @@ -0,0 +1,161 @@ +/* + * 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + + return 0; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/devtree.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/devtree.c new file mode 100644 index 0000000000..1f824d2f15 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include + +/* + * 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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/dma.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/dma.c new file mode 100644 index 0000000000..f61810532a --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include +#include +#include + +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)); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/flat.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/flat.c new file mode 100644 index 0000000000..e8eb4595f1 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +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; + } +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/head.S b/target/linux/ubicom32/files/arch/ubicom32/kernel/head.S new file mode 100644 index 0000000000..0c60504af5 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/head.S @@ -0,0 +1,273 @@ +/* + * arch/ubicom32/kernel/head.S + * + * + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#define __ASM__ +#include + + +#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 - Old 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 - KERNELSTART) + +.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 0x3FFC0030 - ~0x3FFC0200 + * I_RANGE2 / D_RANGE2 - slab area + * typically 0x40A00000 - ~0x44000000 + * I_RANGE3 + * old system call interface if enabled. + * + * D_RANGE3, D_RANGE4 - 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(SDRAMSTART + CONFIG_MIN_RAMSIZE-4) + lea.4 SRC_AN, %lo(SDRAMSTART + CONFIG_MIN_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(__syscall_text_run_begin) + lea.4 SRC_AN, %lo(__syscall_text_run_begin)(SRC_AN) + move.4 I_RANGE1_LO, SRC_AN + moveai SRC_AN, #%hi(__syscall_text_run_end) + lea.4 SRC_AN, %lo(__syscall_text_run_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 + +#ifdef CONFIG_OLD_40400010_SYSTEM_CALL + ;; create a small hole for old syscall location + moveai SRC_AN, #%hi(0x40400000) + lea.4 I_RANGE3_LO, 0x10(SRC_AN) + lea.4 I_RANGE3_HI, 0x14(SRC_AN) +#endif + ;; 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 +#ifdef CONFIG_OLD_40400010_SYSTEM_CALL + move.4 I_RANGE3_EN, #-1 +#else + move.4 I_RANGE3_EN, #0 +#endif + ;; 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 .syscall_text +; + moveai DST_AN, #%hi(__syscall_text_run_end) + lea.4 TMP_DN, %lo(__syscall_text_run_end)(DST_AN) + moveai DST_AN, #%hi(__syscall_text_run_begin) + lea.4 DST_AN, %lo(__syscall_text_run_begin)(DST_AN) + moveai SRC_AN, #%hi(__syscall_text_load_begin) + lea.4 SRC_AN, %lo(__syscall_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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/init_task.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/init_task.c new file mode 100644 index 0000000000..ff0634424d --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///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) }; diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/irq.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/irq.c new file mode 100644 index 0000000000..c041f23e25 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/irq.c @@ -0,0 +1,597 @@ +/* + * arch/ubicom32/kernel/irq.c + * Ubicom32 architecture IRQ support. + * + * (C) Copyright 2009, Ubicom, Inc. + * (C) Copyright 2007, Greg Ungerer + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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. + * affinity container allocated at boot + */ +static void __irq_set_affinity(unsigned int irq, const struct cpumask *dest) +{ + smp_set_affinity(irq, dest); + cpumask_copy(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 = current_thread_info(); + + IRQ_DECLARE_MEASUREMENT; + + /* + * Mark that we are inside of an interrupt and + * that interrupts are disabled. + */ + oldregs = set_irq_regs(regs); + ti->interrupt_nesting++; + trace_hardirqs_off(); + irq_kernel_stack_check(irq, regs); + + /* + * Start the interrupt sequence + */ + irq_enter(); + + /* + * Execute the IRQ handler and any pending SoftIRQ requests. + */ + BUG_ON(!irqs_disabled()); + IRQ_MEASUREMENT_START(); + __do_IRQ(irq); + IRQ_MEASUREMENT_END(irq); + BUG_ON(!irqs_disabled()); + + /* + * TODO: Since IRQ's are disabled when calling irq_exit() + * modify Kconfig to set __ARCH_IRQ_EXIT_IRQS_DISABLED flag. + * This will slightly improve performance by enabling + * softirq handling to avoid disabling/disabled interrupts. + */ + IRQ_MEASUREMENT_START(); + irq_exit(); + IRQ_MEASUREMENT_END(NR_IRQS); + BUG_ON(!irqs_disabled()); + + /* + * Outside of an interrupt (or nested exit). + */ + set_irq_regs(oldregs); + trace_hardirqs_on(); + 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(); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/ldsr.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/ldsr.c new file mode 100644 index 0000000000..a608d74cfe --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/ldsr.c @@ -0,0 +1,1185 @@ +/* + * 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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) { + /* + * No CPU to deliver to so just leave + * the interrupt unmasked and increase + * the backout count. We will eventually + * return and deliver it again. + */ + ldsr_unmask_vector(vector); + ldsr_interrupt.backout++; + continue; + } + 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); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/module.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/module.c new file mode 100644 index 0000000000..3d29dc2b83 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * 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); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/os_node.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/os_node.c new file mode 100644 index 0000000000..9e014d5cb0 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/os_node.c @@ -0,0 +1,88 @@ +/* + * arch/ubicom32/kernel/os_node.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 . + * + */ +#include "linux/types.h" +#include "linux/linkage.h" +#include "linux/uts.h" +#include "linux/utsrelease.h" +#include "linux/version.h" +#include +#include +#include + +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; + unsigned long expected_os_syscall_begin; + unsigned long expected_os_syscall_end; +}; + + +extern void __os_syscall_begin; +extern void __os_syscall_end; +/* + * 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 = 0x10002, + .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 = KERNELSTART, + .expected_ram_end = SDRAMSTART + CONFIG_MIN_RAMSIZE, + .arch_version = UBICOM32_ARCH_VERSION, + .expected_os_syscall_begin = (unsigned long)&__os_syscall_begin, + .expected_os_syscall_end = (unsigned long)&__os_syscall_end, + + +}; diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/process.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/process.c new file mode 100644 index 0000000000..23872fed0f --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/process.c @@ -0,0 +1,634 @@ +/* + * 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 + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DUMP_RANGE_REGISTER(REG, IDX) asm volatile ( \ + " move.4 %0, "REG"_RANGE"IDX"_EN \n\t" \ + " move.4 %1, "REG"_RANGE"IDX"_LO \n\t" \ + " move.4 %2, "REG"_RANGE"IDX"_HI \n\t" \ + : "=d"(en), "=d"(lo), "=d"(hi) \ + ); \ + printk(KERN_NOTICE REG"Range"IDX": en:%08x, range: %08x-%08x\n", \ + (unsigned int)en, \ + (unsigned int)lo, \ + (unsigned int)hi) + +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(); + } +} + +/* + * address_is_valid() + * check if an address is valid -- (for read access) + */ +static bool address_is_valid(const void *address) +{ + int addr = (int)address; + unsigned long socm, eocm, sdram, edram; + + if (addr & 3) + return false; + + processor_ocm(&socm, &eocm); + processor_dram(&sdram, &edram); + if (addr >= socm && addr < eocm) + return true; + + if (addr >= sdram && addr < edram) + return true; + + return false; +} + +/* + * vma_path_name_is_valid() + * check if path_name of a vma is a valid string + */ +static bool vma_path_name_is_valid(const char *str) +{ +#define MAX_NAME_LEN 256 + int i = 0; + if (!address_is_valid(str)) + return false; + + for (; i < MAX_NAME_LEN; i++, str++) { + if (*str == '\0') + return true; + } + + return false; +} + +/* + * show_vmas() + * show vma info of a process + */ +void show_vmas(struct task_struct *task) +{ +#ifdef CONFIG_DEBUG_VERBOSE +#define UBICOM32_MAX_VMA_COUNT 1024 + + struct vm_area_struct *vma; + struct file *file; + char *name = ""; + int flags, loop = 0; + + printk(KERN_NOTICE "Start of vma list\n"); + + if (!address_is_valid(task) || !address_is_valid(task->mm)) + goto error; + + vma = task->mm->mmap; + while (vma) { + if (!address_is_valid(vma)) + goto error; + + flags = vma->vm_flags; + file = vma->vm_file; + + if (file) { + /* seems better to use dentry op here, but sanity check is easier this way */ + if (!address_is_valid(file) || !address_is_valid(file->f_path.dentry) || !vma_path_name_is_valid(file->f_path.dentry->d_name.name)) + goto error; + + name = (char *)file->f_path.dentry->d_name.name; + } + + /* Similar to /proc/pid/maps format */ + printk(KERN_NOTICE "%08lx-%08lx %c%c%c%c %08lx %s\n", + vma->vm_start, + vma->vm_end, + flags & VM_READ ? 'r' : '-', + flags & VM_WRITE ? 'w' : '-', + flags & VM_EXEC ? 'x' : '-', + flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p', + vma->vm_pgoff << PAGE_SHIFT, + name); + + vma = vma->vm_next; + + if (loop++ > UBICOM32_MAX_VMA_COUNT) + goto error; + } + + printk(KERN_NOTICE "End of vma list\n"); + return; + +error: + printk(KERN_NOTICE "\nCorrupted vma list, abort!\n"); +#endif +} + +/* + * show_regs() + * Print out all of the registers. + */ +void show_regs(struct pt_regs *regs) +{ + unsigned int i; + unsigned int en, lo, hi; + + 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); + + /* + * Dump range registers + */ + DUMP_RANGE_REGISTER("I", "0"); + DUMP_RANGE_REGISTER("I", "1"); + DUMP_RANGE_REGISTER("I", "2"); + DUMP_RANGE_REGISTER("I", "3"); + DUMP_RANGE_REGISTER("D", "0"); + DUMP_RANGE_REGISTER("D", "1"); + DUMP_RANGE_REGISTER("D", "2"); + DUMP_RANGE_REGISTER("D", "3"); + DUMP_RANGE_REGISTER("D", "4"); + + 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(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; +} + + +/* + * Infrequently used interface to dump task registers to core files. + */ +int dump_task_regs(struct task_struct *task, elf_gregset_t *elfregs) +{ + struct pt_regs *regs = task_pt_regs(task); + *(struct pt_regs *)elfregs = *regs; + + return 1; +} + +/* + * __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)); diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/processor.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/processor.c new file mode 100644 index 0000000000..55d1bdf623 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/processor.c @@ -0,0 +1,348 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct procnode { + struct devtree_node dn; + unsigned int threads; + unsigned int timers; + unsigned int frequency; + unsigned int ddr_frequency; + unsigned int interrupt0; + unsigned int interrupt1; + void *socm; + void *eocm; + void *sdram; + void *edram; + unsigned int arch_version; + void *os_syscall_begin; + void *os_syscall_end; +}; + +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.0 MHz\n", + clockfreq / 1000000); + seq_printf(m, "DDR Freq:\t%u.0 MHz\n", + pn ? pn->ddr_frequency / 1000000 : 0); + 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 start and end of OCM available to Linux. + */ +void processor_ocm(unsigned long *socm, unsigned long *eocm) +{ + *socm = (unsigned long)pn->socm; + *eocm = (unsigned long)pn->eocm; +} + +/* + * processor_dram() + * Returns the start and end of dram available to Linux. + */ +void processor_dram(unsigned long *sdram, unsigned long *edram) +{ + *sdram = (unsigned long)pn->sdram; + *edram = (unsigned long)pn->edram; +} + +/* + * processor_validate_failed() + * Returns the dram available to Linux. + */ +static noinline void processor_validate_failed(void) +{ + while (1) + THREAD_STALL; +} + +/* + * processor_validate() + * Validates the procnode against limitations of this link/built. + */ +static void processor_validate(void) +{ + void *dram_start = (void *)(KERNELSTART); + void *dram_end = (void *)(SDRAMSTART + CONFIG_MIN_RAMSIZE); +#if APP_OCM_CODE_SIZE || APP_OCM_DATA_SIZE + void *ocm_code_start = (void *)(OCMSTART + APP_OCM_CODE_SIZE); + void *ocm_data_end = (void *)(OCMEND - APP_OCM_DATA_SIZE); +#endif + extern void __os_syscall_begin; + extern void __os_syscall_end; + int proc_node_valid = 1; + + if (!pn) { + printk(KERN_ERR "ERROR: processor node not found\n"); + goto error; + } + + + if (dram_start < pn->sdram || dram_end > pn->edram) { + printk(KERN_ERR "ERROR: processor dram mismatch %p-%p " + "available but we are expecting %p-%p\n", + pn->sdram, pn->edram, dram_start, dram_end); + proc_node_valid = 0; + } else { + printk(KERN_ERR "processor dram %p-%p, expecting %p-%p\n", + pn->sdram, pn->edram, dram_start, dram_end); + } + if (&__os_syscall_begin < pn->os_syscall_begin || + &__os_syscall_end > pn->os_syscall_end) { + printk(KERN_ERR "ERROR: processor syscall area mismatch " + "%p-%p available but we are expecting %p-%p\n", + pn->os_syscall_begin, pn->os_syscall_end, + &__os_syscall_begin, &__os_syscall_end); + proc_node_valid = 0; + } else { + printk(KERN_ERR "processor dram %p-%p, expecting %p-%p\n", + pn->sdram, pn->edram, dram_start, dram_end); + } +#if APP_OCM_CODE_SIZE || APP_OCM_DATA_SIZE + if (ocm_code_start < pn->socm || ocm_data_end > pn->eocm) { + printk(KERN_ERR "ERROR: processor ocm mismatch %p-%p " + "available but we are expecting %p-%p\n", + pn->socm, pn->eocm, ocm_code_start, ocm_data_end); + proc_node_valid = 0; + } else { + printk(KERN_INFO "processor ocm %p-%p, expecting %p-%p\n", + pn->socm, pn->eocm, ocm_code_start, ocm_data_end); + + } +#endif + + if (UBICOM32_ARCH_VERSION != pn->arch_version) { + printk(KERN_ERR "ERROR: processor arch mismatch, kernel" + "compiled for %d found %d\n", + UBICOM32_ARCH_VERSION, pn->arch_version); + proc_node_valid = 0; + } + + if (proc_node_valid) + return; +error: + processor_validate_failed(); +} + +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"); + + processor_validate(); + + /* + * If necessary correct the initial range registers to cover the + * complete physical space + */ + if (pn->edram > (void *)(SDRAMSTART + CONFIG_MIN_RAMSIZE)) { + printk(KERN_INFO "updating range registers for expanded dram\n"); + asm volatile ( + " move.4 D_RANGE1_HI, %0 \t\n" + " move.4 I_RANGE0_HI, %0 \t\n" +#ifdef CONFIG_PROTECT_KERNEL + " move.4 D_RANGE2_HI, %0 \t\n" + " move.4 I_RANGE2_HI, %0 \t\n" +#endif + : : "a"((unsigned long)pn->edram - 4) + ); + } + +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/ptrace.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/ptrace.c new file mode 100644 index 0000000000..18bb39e9d9 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * 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; + } +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/semaphore.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/semaphore.c new file mode 100644 index 0000000000..d996ac2b30 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +/* + * Generic semaphore code. Buyer beware. Do your own + * specific changes in + */ + +#include +#include +#include +#include + +#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 + * 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); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/setup.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/setup.c new file mode 100644 index 0000000000..7357f4ee35 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/setup.c @@ -0,0 +1,194 @@ +/* + * 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 + * Copyleft ()) 2000 James D. Schettine {james@telos-systems.com} + * Copyright (C) 1998 Kenneth Albanowski + * Copyright (C) 1995 Hamish Macdonald + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * Copyright (C) 2001 Lineo, Inc. + * 68VZ328 Fixes/support Evan Stawnyczy + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + unsigned long ram_start; + + processor_init(); + bootargs_init(); + + /* + * Use the link for memory_start from the link and the processor + * node for memory_end. + */ + memory_start = PAGE_ALIGN(((unsigned long)&_end)); + processor_dram(&ram_start, &memory_end); + + 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; + + /* + * bootexec copies the original default command line to end of memory. + * u-boot can modify it there (i.e. to enable network boot) and the + * kernel picks up the modified version. + * + * mainexec creates a `new default' command_line which is in the + * bootargs devnode. It is updated on every firmware update but + * not used at the moment. + */ + strlcpy(boot_command_line, (char *)(memory_end - COMMAND_LINE_SIZE), 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. \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 + +#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()); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/signal.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/signal.c new file mode 100644 index 0000000000..f6ccbe3a75 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/smp.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/smp.c new file mode 100644 index 0000000000..4aa27eb444 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/smp.c @@ -0,0 +1,806 @@ +/* + * arch/ubicom32/kernel/smp.c + * SMP implementation for Ubicom32 processors. + * + * (C) Copyright 2009, Ubicom, Inc. + * Copyright (C) 1999 Walt Drummond + * Copyright (C) 1999 David Mosberger-Tang + * Copyright (C) 2001,2004 Grant Grundler + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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, +}; + +/* + * We maintain a hardware thread oriented view of online threads + * and those involved or needing IPI. + */ +static volatile unsigned long smp_online_threads = 0; +static volatile unsigned long smp_needs_ipi = 0; +static volatile 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 &= ~(1 << 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); + smp_debug(100, KERN_INFO "cpu[%d]: send: %d\n", cpu, op); +} + +/* + * ipi_send_mask + * Send an IPI to each cpu in mask. + */ +static inline void ipi_send_mask(unsigned int op, const struct cpumask 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(); + struct cpumask result; + cpumask_copy(&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); + smp_debug(100, KERN_INFO "cpu[%d]: unamesk vector\n", smp_processor_id()); +} + +/* + * 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_debug(100, KERN_INFO "smp: reset IPIs for: 0x%x\n", mask); +} + +/* + * 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, const struct cpumask *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(const struct cpumask *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_mask(const struct cpumask *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); +} + +/* + * setup_profiling_timer() + * Dummy function created to keep Oprofile happy in the SMP case. + */ +int setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* + * 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); + int i; + for_each_cpu_mask(i, CPU_MASK_ALL) + set_cpu_possible(i, true); + + current_thread_info()->cpu = 0; + p->tid = thread_get_self(); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/stacktrace.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/stacktrace.c new file mode 100644 index 0000000000..2a10e3f4f1 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/stacktrace.c @@ -0,0 +1,244 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include + +/* + * 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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/sys_ubicom32.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/sys_ubicom32.c new file mode 100644 index 0000000000..b06f3f3965 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* 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; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/syscalltable.S b/target/linux/ubicom32/files/arch/ubicom32/kernel/syscalltable.S new file mode 100644 index 0000000000..8921fb83da --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/syscalltable.S @@ -0,0 +1,376 @@ +/* + * arch/ubicom32/kernel/syscalltable.S + * + * + * (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 . + * + * 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 , Kenneth Albanowski , + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include + +.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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/thread.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/thread.c new file mode 100644 index 0000000000..aaa5fbea4e --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/time.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/time.c new file mode 100644 index 0000000000..4a99284bd0 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include + +/* + * 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 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 +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/timer_broadcast.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/timer_broadcast.c new file mode 100644 index 0000000000..8f0cdc4d55 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + * arch/arm + * arch/sh + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/timer_device.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/timer_device.c new file mode 100644 index 0000000000..d2d094d37a --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/timer_device.c @@ -0,0 +1,301 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SMP) +#include +#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, const 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; + cpumask_copy(&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)); +} +#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_PTR); + +#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_PTR); +#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_PTR); + } +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/timer_tick.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/timer_tick.c new file mode 100644 index 0000000000..7a2ad49498 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include + +#include +#include +#if defined(CONFIG_SMP) +#include +#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); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/topology.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/topology.c new file mode 100644 index 0000000000..0676a16587 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include + +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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/traps.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/traps.c new file mode 100644 index 0000000000..8cb22e25ea --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/traps.c @@ -0,0 +1,514 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +/* + * Sets up all exception vectors + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + +extern void show_vmas(struct task_struct *task); + +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 code_end; + 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; + + processor_dram(&code_start, &code_end); + + /* + * 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"); + show_vmas(task); + } + 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, KERNELSTART, memory_end) && + (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; + } +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/uaccess.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/uaccess.c new file mode 100644 index 0000000000..2fe5f5f87c --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include + +#include +#include + +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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_context_switch.S b/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_context_switch.S new file mode 100644 index 0000000000..08db4c057a --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_context_switch.S @@ -0,0 +1,359 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include + +/* + * begin_restore_context() + * Restore most of the context from sp (struct pt_reg *) + * + * This *can* be called without the global atomic lock. (because sp is + * not restored!) Only d15 and a3 are allowed to be used after this + * before calling complete_restore_context + */ +.macro begin_restore_context + move.4 d0, PT_D0(sp) + move.4 d1, PT_D1(sp) + move.4 d2, PT_D2(sp) + move.4 d3, PT_D3(sp) + move.4 d4, PT_D4(sp) + move.4 d5, PT_D5(sp) + move.4 d6, PT_D6(sp) + move.4 d7, PT_D7(sp) + move.4 d8, PT_D8(sp) + move.4 d9, PT_D9(sp) + move.4 d10, PT_D10(sp) + move.4 d11, PT_D11(sp) + move.4 d12, PT_D12(sp) + move.4 d13, PT_D13(sp) + move.4 d14, PT_D14(sp) +;; move.4 d15, PT_D15(sp) + move.4 a0, PT_A0(sp) + move.4 a1, PT_A1(sp) + move.4 a2, PT_A2(sp) +;; move.4 a3, PT_A3(sp) + move.4 a4, PT_A4(sp) + move.4 a5, PT_A5(sp) + move.4 a6, PT_A6(sp) + move.4 acc0_hi, PT_ACC0HI(sp) + move.4 acc0_lo, PT_ACC0LO(sp) + move.4 mac_rc16, PT_MAC_RC16(sp) + move.4 acc1_hi, PT_ACC1HI(sp) + move.4 acc1_lo, PT_ACC1LO(sp) + move.4 source3, PT_SOURCE3(sp) + move.4 int_mask0, PT_INT_MASK0(sp) + move.4 int_mask1, PT_INT_MASK1(sp) +.endm + +/* + * complete_restore_context() + * Completely restore the 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 complete_restore_context + move.4 a3, sp + move.4 d15, PT_D15(sp) + move.4 sp, PT_SP(a3) ; Recover Stack pointer from save area + move.4 -4(sp)++, PT_PC(a3) ; Recover saved PC and save to stack + move.4 -4(sp)++, PT_CSR(a3) ; Recover saved csr and save to stack + move.4 a3, PT_A3(a3) +.endm + +/* + * old restore_context macro + */ +.macro restore_context + begin_restore_context + complete_restore_context +.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, "ax", @progbits + .global ret_from_interrupt_to_kernel +ret_from_interrupt_to_kernel: + begin_restore_context ; Restore the thread context + atomic_lock_acquire ; Enter critical section + complete_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 critical section handling in this code? + * + */ + .section .text.ret_from_interrupt_to_user, "ax", @progbits + .global ret_from_interrupt_to_user +ret_from_interrupt_to_user: + ldsr_thread_enable_interrupts ; enable the threads interrupts + /* + * Set a1 to the thread info pointer, no need to save it as we are + * restoring userspace and will never return + */ + movei d0, #(~(ASM_THREAD_SIZE-1)) + and.4 a1, sp, d0 + + /* + * Test if the scheduler needs to be called. + */ + btst TI_FLAGS(a1), #ASM_TIF_NEED_RESCHED + jmpeq.t 2f + call a5, schedule ; Call the scheduler. I will come back here. + + /* + * See if we have pending signals and call do_signal + * if needed. + */ +2: + btst TI_FLAGS(a1), #ASM_TIF_SIGPENDING ; Any signals needed? + jmpeq.t 1f + + /* + * Now call do_signal() + */ + move.4 d0, #0 ; oldset pointer is NULL + move.4 d1, sp ; d1 is the regs pointer + call a5, do_signal ; Call do_signal() + + /* + * Back from do_signal(), re-enter critical section. + */ +1: + begin_restore_context ; Restore the thread context + atomic_lock_acquire ; Enter critical section + call a3, __complete_and_return_to_userspace ; jump to unprotected section + +/* + * 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. + */ + .section .text.restore_all_registers, "ax", @progbits + .global restore_all_registers +restore_all_registers: + begin_restore_context ; Restore the thread context + atomic_lock_acquire ; Enter critical section + call a3, __complete_and_return_to_userspace + +/* + * __complete_and_return_to_userspace + * + * restores the second half of the context and returns + * You must have the atomic lock when you call this function + */ + .section .kernel_unprotected, "ax", @progbits +__complete_and_return_to_userspace: + disable_kernel_ranges_for_current d15 ; disable kernel ranges + complete_restore_context ; restore previous context + atomic_lock_release ; Leave critical section + 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 .text.ret_from_fork, "ax", @progbits + .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) + call a5, schedule_tail + + 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 + ;; I think atomic_lock_acquire could be moved here.. + 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. + +#ifdef CONFIG_PROTECT_KERNEL + call a3, __ret_from_fork_bottom_half + .section .kernel_unprotected, "ax", @progbits +__ret_from_fork_bottom_half: + disable_kernel_ranges_for_current d15 +#endif + 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) + */ + .section .text.__switch_to, "ax", @progbits + .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 - bit's 6 through 31 are zeroes anyway. + move.4 (a4, d15), a3 ; Load the thread info pointer into the hw_ksp array.. + + /* + * We are done with context switch. Time to return.. + */ + calli a5, 0(a5) + .size __switch_to, . - __switch_to + +/* + * 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); + */ + .section .text.ubicom32_emulate_insn, "ax", @progbits + .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 + .size ubicom32_emulate_insn, . - ubicom32_emulate_insn diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_ksyms.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_ksyms.c new file mode 100644 index 0000000000..ea7eb1575a --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_ksyms.c @@ -0,0 +1,98 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* 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); +EXPORT_SYMBOL(memmove); + +#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 __divdi3(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(__divdi3); +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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_syscall.S b/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_syscall.S new file mode 100644 index 0000000000..870f66c8f4 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_syscall.S @@ -0,0 +1,694 @@ +/* + * arch/ubicom32/kernel/ubicom32_syscall.S + * + * + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +#include +#include +#include +#include + +/* + * __old_system_call() + */ + .section .old_syscall_entry.text, "ax", @progbits +#ifdef CONFIG_OLD_40400010_SYSTEM_CALL +__old_system_call: + call a3, system_call + .size __old_system_call, . - __old_system_call ; +#else + /* + * something that will crash the userspace application, but + * should not take down the kernel, if protection is enabled + * this will never even get executed. + */ + .long 0xFABBCCDE ; illegal instruction + bkpt #-1 ; we will never get here +#endif + +/* + * system_call() + */ + .section .syscall_entry.text, "ax", @progbits + .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 + /* + * in order to reduce the size of code in the syscall section we get + * out of it right now + */ + call a4, __system_call_bottom_half + .size system_call, . - system_call + + .section .text.__system_call_bottom_half, "ax", @progbits +__system_call_bottom_half: + + /* + * 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 + 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) + */ + call a5, syscall_trace + + /* + * 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_bottom_half, . - __system_call_bottom_half + +/* + * syscall_exit() + */ + .section .text.syscall_exit + .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 + + call a4, __syscall_exit_bottom_half + + .section .kernel_unprotected, "ax", @progbits +__syscall_exit_bottom_half: + /* + * 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. + */ + .section .text.syscall_exit, "ax", @progbits +.Lsyscall_exit__post_processing: + btst TI_FLAGS(a1), #ASM_TIF_SYSCALL_TRACE + jmpeq.t 1f + call a5, syscall_trace + + /* + * 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 + call a5, schedule + + /* + * 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. + call a5, do_signal + + 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, "ax", @progbits + .global kernel_execve +kernel_execve: + move.4 -4(sp)++, a5 ; Save return address + /* + * Call execve + */ + movei d8, #__NR_execve ; call execve + call a5, system_call + 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. + */ + call a3, __enable_kernel_ranges ; and jump back to kernel +#else + ret a5 ; jump back to the kernel +#endif + + .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, "ax", @progbits + .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 + call a5, system_call ; as we are 'in' the kernel + ; we can call kernel_syscall + + 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, "ax", @progbits + .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. + */ + call a5, __enable_kernel_ranges +#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 + +#ifdef CONFIG_PROTECT_KERNEL + .section .kernel_unprotected, "ax", @progbits +__enable_kernel_ranges: + atomic_lock_acquire ; Enter critical section + enable_kernel_ranges_for_current d15 + atomic_lock_release ; Leave critical section + calli a5, 0(a5) + .size __enable_kernel_ranges, . - __enable_kernel_ranges + +#endif + +/* + * The following system call intercept functions where we setup the + * input to the real system call. In all cases these are just taking + * the current sp which is pointing to pt_regs and pushing it into the + * last arg of the system call. + * + * i.e. the public definition of sys_execv is + * sys_execve( char *name, + * char **argv, + * char **envp ) + * but process.c defines it as + * sys_execve( char *name, + * char **argv, + * char **envp, + * struct pt_regs *regs ) + * + * so execve_intercept needs to populate the 4th arg with pt_regs*, + * which is the stack pointer as we know we must be coming out of + * system_call + * + * The intercept vectors are referenced by syscalltable.S + */ + +/* + * execve_intercept() + */ + .section .text.execve_intercept, "ax", @progbits + .global execve_intercept +execve_intercept: + move.4 d3, sp ; Save pt_regs address + call a3, sys_execve + + .size execve_intercept, . - execve_intercept + +/* + * vfork_intercept() + */ + .section .text.vfork_intercept, "ax", @progbits + .global vfork_intercept +vfork_intercept: + move.4 d0, sp ; Save pt_regs address + call a3, sys_vfork + + .size vfork_intercept, . - vfork_intercept + +/* + * clone_intercept() + */ + .section .text.clone_intercept, "ax", @progbits + .global clone_intercept +clone_intercept: + move.4 d2, sp ; Save pt_regs address + call a3, sys_clone + + .size clone_intercept, . - clone_intercept + +/* + * sys_sigsuspend() + */ + .section .text.sigclone_intercept, "ax", @progbits + .global sys_sigsuspend +sys_sigsuspend: + move.4 d0, sp ; Pass pointer to pt_regs in d0 + call a3, do_sigsuspend + + .size sys_sigsuspend, . - sys_sigsuspend + +/* + * sys_rt_sigsuspend() + */ + .section .text.sys_rt_sigsuspend, "ax", @progbits + .global sys_rt_sigsuspend +sys_rt_sigsuspend: + move.4 d0, sp ; Pass pointer to pt_regs in d0 + call a3, do_rt_sigsuspend + + .size sys_rt_sigsuspend, . - sys_rt_sigsuspend + +/* + * sys_rt_sigreturn() + */ + .section .text.sys_rt_sigreturn, "ax", @progbits + .global sys_rt_sigreturn +sys_rt_sigreturn: + move.4 d0, sp ; Pass pointer to pt_regs in d0 + call a3, do_rt_sigreturn + + .size sys_rt_sigreturn, . - sys_rt_sigreturn + +/* + * sys_sigaltstack() + */ + .section .text.sys_sigaltstack, "ax", @progbits + .global sys_sigaltstack +sys_sigaltstack: + move.4 d0, sp ; Pass pointer to pt_regs in d0 + call a3, do_sys_sigaltstack + + .size sys_sigaltstack, . - sys_sigaltstack diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/unaligned_trap.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/unaligned_trap.c new file mode 100644 index 0000000000..d856d061dc --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include + +#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; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/vmlinux.lds.S b/target/linux/ubicom32/files/arch/ubicom32/kernel/vmlinux.lds.S new file mode 100644 index 0000000000..cd646772cf --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/kernel/vmlinux.lds.S @@ -0,0 +1,370 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include + +/* + * 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 + +/* + * 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 KERNELSTART +#define RAM_LENGTH ((SDRAMSTART + CONFIG_MIN_RAMSIZE) - RAM_START) +#define TEXT ram +#define DATA ram +#define INIT ram +#define BSS ram + +#ifndef DATA_ADDR +#define DATA_ADDR +#endif + +#include + +OUTPUT_ARCH(ubicom32) +ENTRY(_start) + +MEMORY { + ram : ORIGIN = RAM_START, LENGTH = RAM_LENGTH + syscall : ORIGIN = OS_SYSCALL_BEGIN, LENGTH = (OS_SYSCALL_END - OS_SYSCALL_BEGIN) + 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 = SDRAMSTART; +__sdram_limit = __sdram_begin + CONFIG_MIN_RAMSIZE; +__filemedia_begin_addr = FLASHSTART; +__filemedia_end_addr = __filemedia_begin_addr + 0x00800000; + +/* + * For internal diagnostics + */ +__os_syscall_begin = OS_SYSCALL_BEGIN; +__os_syscall_end = OS_SYSCALL_END; + +SECTIONS { + + .fixed_text : { + _begin = .; + *(.skip_syscall) + *(.old_syscall_entry.text) + __fixed_text_end = .; + } > TEXT + . = _begin + SIZEOF(.fixed_text) ; + + /* + * System call text in lower ocm (fixed location, can never change) + */ + __syscall_text_load_begin = .; + __syscall_text_run_begin = OS_SYSCALL_BEGIN; + + .syscall_text __syscall_text_run_begin : AT(__syscall_text_load_begin) { + *(.syscall_entry.text) /* Must be at OS_SYSCALL_BEGIN 0x3ffc0040 */ + *(.kernel_unprotected) + . = ALIGN(4); + __syscall_text_run_end = .; + } > syscall /* .syscall_text */ + . = __syscall_text_load_begin + __syscall_text_run_end - __syscall_text_run_begin ; + __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 + . = 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 = .; + + *(.rodata) *(.rodata.*) + *(__vermagic) /* Kernel version magic */ + *(__markers_strings) + *(.rodata1) + *(.rodata.str1.1) + *(__tracepoints_strings) + + /* PCI quirks */ + __start_pci_fixups_early = . ; + *(.pci_fixup_early) + __end_pci_fixups_early = . ; + __start_pci_fixups_header = . ; + *(.pci_fixup_header) + __end_pci_fixups_header = . ; + __start_pci_fixups_final = . ; + *(.pci_fixup_final) + __end_pci_fixups_final = . ; + __start_pci_fixups_enable = . ; + *(.pci_fixup_enable) + __end_pci_fixups_enable = . ; + __start_pci_fixups_resume = . ; + *(.pci_fixup_resume) + __end_pci_fixups_resume = . ; + __start_pci_fixups_resume_early = . ; + *(.pci_fixup_resume_early) + __end_pci_fixups_resume_early = . ; + __start_pci_fixups_suspend = . ; + *(.pci_fixup_suspend) + __end_pci_fixups_suspend = . ; + + __start_builtin_fw = . ; + *(.builtin_fw) + __end_builtin_fw = . ; + + + /* Kernel symbol table: Normal symbols */ + . = ALIGN(4); + __start___ksymtab = .; + *(__ksymtab) + __stop___ksymtab = .; + + /* Kernel symbol table: GPL-only symbols */ + __start___ksymtab_gpl = .; + *(__ksymtab_gpl) + __stop___ksymtab_gpl = .; + + /* Kernel symbol table: Normal unused symbols */ + __start___ksymtab_unused = .; + *(__ksymtab_unused) + __stop___ksymtab_unused = .; + + /* Kernel symbol table: GPL-only unused symbols */ + __start___ksymtab_unused_gpl = .; + *(__ksymtab_unused_gpl) + __stop___ksymtab_unused_gpl = .; + + /* Kernel symbol table: GPL-future symbols */ + __start___ksymtab_gpl_future = .; + *(__ksymtab_gpl_future) + __stop___ksymtab_gpl_future = .; + + /* Kernel symbol table: Normal symbols */ + __start___kcrctab = .; + *(__kcrctab) + __stop___kcrctab = .; + + /* Kernel symbol table: GPL-only symbols */ + __start___kcrctab_gpl = .; + *(__kcrctab_gpl) + __stop___kcrctab_gpl = .; + + /* Kernel symbol table: GPL-future symbols */ + __start___kcrctab_gpl_future = .; + *(__kcrctab_gpl_future) + __stop___kcrctab_gpl_future = .; + + /* Kernel symbol table: strings */ + *(__ksymtab_strings) + + /* Built-in module parameters */ + . = ALIGN(4) ; + __start___param = .; + *(__param) + __stop___param = .; + + . = ALIGN(4) ; + _etext = . ; + } > TEXT + + .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 + + /DISCARD/ : { + EXIT_TEXT + EXIT_DATA + *(.exitcall.exit) + } + + .bss : { + . = ALIGN(4); + _sbss = . ; + *(.bss) + *(COMMON) + . = ALIGN(4) ; + _ebss = . ; + _end = . ; + } > BSS + + NOTES > BSS + +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/lib/Makefile b/target/linux/ubicom32/files/arch/ubicom32/lib/Makefile new file mode 100644 index 0000000000..e7f41ccf3d --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/lib/Makefile @@ -0,0 +1,32 @@ +# +# arch/ubicom32/lib/Makefile +# +# +# (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 . +# +# 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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/lib/checksum.c b/target/linux/ubicom32/files/arch/ubicom32/lib/checksum.c new file mode 100644 index 0000000000..c93920f7cb --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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, + * Arnt Gulbrandsen, + * Tom May, + * Andreas Schwab, + * 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 +#include + +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); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/lib/delay.c b/target/linux/ubicom32/files/arch/ubicom32/lib/delay.c new file mode 100644 index 0000000000..d19f97f09e --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include + +/* + * 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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/lib/mem_ubicom32.c b/target/linux/ubicom32/files/arch/ubicom32/lib/mem_ubicom32.c new file mode 100644 index 0000000000..d9c302ec64 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/lib/mem_ubicom32.c @@ -0,0 +1,343 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include + +#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; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + char *tmp; + const char *s; + + if (n == 0) + return dest; + + tmp = dest; + s = src; + + /* + * Will perform 16-bit move if possible + */ + if (likely((((u32)dest | (u32)src | n) & 1) == 0)) { + if (dest <= src) { + asm volatile ( + " sub.4 d15, #0, %2 \n\t" // set up for jump table + " and.4 d15, #(32-2), d15 \n\t" // d15 = (- count) & (32 - 2) + " moveai a3, #%%hi(1f) \n\t" + " lea.1 a3, %%lo(1f)(a3) \n\t" + " lea.2 a3, (a3,d15) \n\t" + " calli a3, 0(a3) \n\t" + + "1: .rept 16 \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" + + : "+a" (tmp), "+a" (s), "+d" (n) + : + : "d15", "a3", "memory", "cc" + ); + } else { + tmp += n; + s += n; + asm volatile ( + " sub.4 d15, #0, %2 \n\t" // set up for jump table + " and.4 d15, #(32-2), d15 \n\t" // d15 = (- count) & (32 - 2) + " moveai a3, #%%hi(1f) \n\t" + " lea.1 a3, %%lo(1f)(a3) \n\t" + " lea.2 a3, (a3,d15) \n\t" + " calli a3, 0(a3) \n\t" + + "1: .rept 16 \n\t" + " move.2 -2(%0)++, -2(%1)++ \n\t" + " .endr \n\t" + " add.4 %2, #-32, %2 \n\t" + " jmpgt.w.f 1b \n\t" + + : "+a" (tmp), "+a" (s), "+d" (n) + : + : "d15", "a3", "memory", "cc" + ); + } + return dest; + } + + if (dest <= src) { + asm volatile ( + " sub.4 d15, #0, %2 \n\t" // set up for jump table + " and.4 d15, #(16-1), d15 \n\t" // d15 = (- count) & (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" + : "+a" (tmp), "+a" (s), "+d" (n) + : + : "d15", "a3", "memory", "cc" + ); + } else { + tmp += n; + s += n; + asm volatile ( + " sub.4 d15, #0, %2 \n\t" // set up for jump table + " and.4 d15, #(16-1), d15 \n\t" // d15 = (- count) & (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 -1(%0)++, -1(%1)++ \n\t" + " .endr \n\t" + " add.4 %2, #-16, %2 \n\t" + " jmpgt.w.f 1b \n\t" + : "+a" (tmp), "+a" (s), "+d" (n) + : + : "d15", "a3", "memory", "cc" + ); + } + return dest; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/Kconfig.switch b/target/linux/ubicom32/files/arch/ubicom32/mach-common/Kconfig.switch new file mode 100644 index 0000000000..0303e1600b --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/Kconfig.switch @@ -0,0 +1,12 @@ +menuconfig UBICOM_SWITCH + tristate "Switch devices" + help + This option provides Ethernet switch management options via proc fs + +if UBICOM_SWITCH +config UBICOM_SWITCH_BCM539X + tristate "Broadcom BCM539X series (SPI)" + depends on SPI_MASTER + help + Supports Broadcom BCM539X Gigabit Ethernet Switches over SPI +endif diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/Makefile b/target/linux/ubicom32/files/arch/ubicom32/mach-common/Makefile new file mode 100644 index 0000000000..8c645aff6a --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/Makefile @@ -0,0 +1,41 @@ +# +# 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 . +# +# 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 profile.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_UBICOM_SWITCH) += switch-core.o +obj-$(CONFIG_UBICOM_SWITCH_BCM539X) += switch-bcm539x.o +obj-$(CONFIG_UIO_UBICOM32RING) += ring_tio.o +obj-$(CONFIG_SND_UBI32) += audio.o +obj-$(CONFIG_UBICOM32_PLIO) += plio.o + diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/audio.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/audio.c new file mode 100644 index 0000000000..37db89093d --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/audio.c @@ -0,0 +1,134 @@ +/* + * arch/ubicom32/mach-common/audio.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 . + */ + +#include +#include + +#include +#include +#include + +/* + * The number of audio devices currently allocated, used for .id + */ +static int __initdata audio_device_count; + +/* + * The maximum number of resources (cards) that the audio will have. + * Currently 3, a register space, and up to 2 interrupts. + */ +#define AUDIO_MAX_RESOURCES 3 + +/* + * audio_device_alloc + * Checks the device tree and allocates a platform_device if found + */ +struct platform_device * __init audio_device_alloc(const char *driver_name, + const char *node_name, const char *inst_name, int priv_bytes) +{ + struct platform_device *pdev; + struct resource *res; + struct audio_node *audio_node; + struct ubi32pcm_platform_data *pdata; + struct audio_dev_regs *adr; + int idx; + + /* + * Check the device tree for the audio node + */ + audio_node = (struct audio_node *)devtree_find_node(node_name); + if (!audio_node) { + printk(KERN_WARNING "audio device '%s' not found\n", node_name); + return NULL; + } + + if (audio_node->version != AUDIONODE_VERSION) { + printk(KERN_WARNING "audio node not compatible\n"); + return NULL; + } + + /* + * Find the instance in this node + */ + adr = audio_node->regs->adr; + for (idx = 0; idx < audio_node->regs->max_devs; idx++) { + if ((adr->version == AUDIO_DEV_REGS_VERSION) && + (strcmp(adr->name, inst_name) == 0)) { + break; + } + adr++; + } + if (idx == audio_node->regs->max_devs) { + printk(KERN_WARNING "audio inst '%s' not found in device '%s'\n", inst_name, node_name); + 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 could not alloc pdev\n"); + return NULL; + } + + res = kzalloc(sizeof(struct resource) * AUDIO_MAX_RESOURCES, + GFP_KERNEL); + if (!res) { + kfree(pdev); + printk(KERN_WARNING "audio could not alloc res\n"); + return NULL; + } + + pdev->name = driver_name; + pdev->id = audio_device_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 + AUDIO_TX_IRQ_RESOURCE].start = audio_node->dn.sendirq; + res[1 + AUDIO_TX_IRQ_RESOURCE].flags = IORESOURCE_IRQ; + res[1 + AUDIO_RX_IRQ_RESOURCE].start = audio_node->dn.recvirq; + res[1 + AUDIO_RX_IRQ_RESOURCE].flags = IORESOURCE_IRQ; + pdev->num_resources = 3; + + printk(KERN_INFO "Audio.%d '%s':'%s' found irq=%d/%d.%d regs=%p pdev=%p/%p\n", + pdev->id, node_name, inst_name, audio_node->dn.sendirq, + audio_node->dn.recvirq, idx, audio_node->regs, pdev, res); + pdata = (struct ubi32pcm_platform_data *)(pdev + 1); + pdev->dev.platform_data = pdata; + pdata->node_name = node_name; + pdata->inst_name = inst_name; + pdata->inst_num = idx; + if (priv_bytes) { + pdata->priv_data = pdata + 1; + } + + return pdev; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/board.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/board.c new file mode 100644 index 0000000000..9634e3413f --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include + +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; + } +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/bootargs.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/bootargs.c new file mode 100644 index 0000000000..2967150987 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include + +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; + } +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/cachectl.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/cachectl.c new file mode 100644 index 0000000000..afb9dc4d4a --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/cachectl.c @@ -0,0 +1,136 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include + +/* + * 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; + int dccr = cc == DCCR_BASE; + if (dccr && 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) + ); + } + + if (dccr) + UBICOM32_LOCK(DCCR_LOCK_BIT); + else + UBICOM32_LOCK(ICCR_LOCK_BIT); + + /* + * 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 (dccr && 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); + } + + if (dccr) + UBICOM32_UNLOCK(DCCR_LOCK_BIT); + else + UBICOM32_UNLOCK(ICCR_LOCK_BIT); + +} +EXPORT_SYMBOL(mem_cache_control); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/common.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/common.c new file mode 100644 index 0000000000..2f183bd10a --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* 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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/io.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/io.c new file mode 100644 index 0000000000..3c55ba771e --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +#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 */ diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/pci.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/pci.c new file mode 100644 index 0000000000..a3a07121d9 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/plio.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/plio.c new file mode 100644 index 0000000000..ac26a1e8d4 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/plio.c @@ -0,0 +1,92 @@ +/* + * plio.c + * PLIO state machine support functions + * + * Copyright © 2009 Ubicom Inc. . 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 . + */ + +#include +#include +#include +#include + +/* + * plio_reset + * Select and reset PLIO function + */ +static void plio_reset(const plio_fctl_t *plio_fctl) { + plio_io_function_t plio_function = { + .fn_sel = PLIO_FN, + .fn_reset = 1, + }; + + /* + * enable extension port + */ + PEXT_NBR->function = plio_function; + + /* + * program clock dividers + */ + PLIO_NBR->fctl2 = plio_fctl->fctl2; + + /* + * select plio function and assert function reset + */ + plio_function.br_thread = thread_get_self(); + plio_function.fn_reset = 1; + PLIO_NBR->function = plio_function; + + /* + * program plio controls + */ + PLIO_NBR->fctl0 = plio_fctl->fctl0; + PLIO_NBR->fctl1 = plio_fctl->fctl1; + + /* + * deassert function reset + */ + plio_function.fn_reset = 0; + PLIO_NBR->function = plio_function; +} + +/* + * plio_init + * configure and initialize PLIO. + */ +void plio_init(const plio_fctl_t *plio_fctl, const plio_config_t *plio_config, const plio_sram_t plio_sram_cfg[], int sram_cfg_size){ + /* + * first reset to start plio clock + */ + plio_reset(plio_fctl); + + udelay(1); + + /* + * configure pfsm + */ + PLIO_NBR->fctl0.pfsm_prog = 1; + memcpy(PLIO_BR->pfsm_sram, plio_sram_cfg, sram_cfg_size); + PLIO_NBR->fctl0.pfsm_prog = 0; + + /* + * program rest of plio + */ + memcpy(&PLIO_BR->config, plio_config, sizeof(plio_config_t)); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/profile.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/profile.c new file mode 100644 index 0000000000..c95de6b17d --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/profile.c @@ -0,0 +1,549 @@ +/* + * arch/ubicom32/mach-common/profile.c + * Implementation for Ubicom32 Profiler + * + * (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 . + */ + +#include +#include "profile.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * spacs for all memory blocks so we can hold locks for short time when walking tables + */ +#define PROFILE_NUM_MAPS 5000 +static struct profile_map profile_pm[PROFILE_NUM_MAPS]; + +static struct profilenode *node = NULL; +static int profile_first_packet = 1; + +static int profile_open(struct inode *inode, struct file *filp) +{ + if (!node) { + return -ENOENT; + } + node->busy = 1; + if (!node->enabled) { + node->enabled = 1; + node->busy = 0; + profile_first_packet = 1; + return 0; + } + node->busy = 0; + return -EBUSY; +} + +static int profile_sequence_num; + +/* + * make a packet full of sample data + */ +static int profile_make_data_packet(char *buf, int count) +{ + int samples; /* number of samples requested */ + int i; + struct profile_header ph; + char *ptr; + + if (count < sizeof(struct profile_header) + sizeof(struct profile_sample)) { + return -EINVAL; + } + + /* + * fill in the packet header + */ + memset(&ph, 0, sizeof(struct profile_header)); + ph.magic = PROF_MAGIC + PROFILE_VERSION; + ph.header_size = sizeof(struct profile_header); + ph.clocks = node->clocks; + for (i = 0; i < PROFILE_MAX_THREADS; ++i) { + ph.instruction_count[i] = node->inst_count[i]; + } + ph.profile_instructions = 0; + ph.enabled = node->enabled_threads; + ph.hrt = node->hrt; + ph.high = 0; + ph.profiler_thread = node->profiler_thread; + ph.clock_freq = node->clock_freq; + ph.seq_num = profile_sequence_num++; + ph.cpu_id = node->cpu_id; + ph.perf_counters[0] = node->stats[0]; + ph.perf_counters[1] = node->stats[1]; + ph.perf_counters[2] = node->stats[2]; + ph.perf_counters[3] = node->stats[3]; + ph.ddr_freq = node->ddr_freq; + + ptr = buf + sizeof(struct profile_header); + + samples = (count - sizeof(struct profile_header)) / sizeof(struct profile_sample); + for (i = 0; i < samples && node->count; ++i) { + if (copy_to_user(ptr, &node->samples[node->tail], sizeof(struct profile_sample)) != 0) { + return -EFAULT; + } + node->count--; + node->tail++; + if (node->tail >= node->max_samples) { + node->tail = 0; + } + ptr += sizeof(struct profile_sample); + } + ph.sample_count = i; + if (copy_to_user(buf, &ph, sizeof(struct profile_header)) != 0) { + return -EFAULT; + } + if (ph.sample_count == 0) + return 0; + else + return sizeof(struct profile_header) + ph.sample_count * sizeof(struct profile_sample); +} + +static void profile_get_memory_stats(unsigned int *total_free, unsigned int *max_free) +{ + struct list_head *p; + struct zone *zone; + unsigned int size; + + *total_free = 0; + *max_free = 0; + + /* + * get all the free regions. In each zone, the array of free_area lists contains the first page of each frame of size 1 << order + */ + for_each_zone(zone) { + unsigned long order, flags, i; + + if (!populated_zone(zone)) + continue; + + if (!is_normal(zone)) + continue; + + spin_lock_irqsave(&zone->lock, flags); + for_each_migratetype_order(order, i) { + size = ((1 << order) << PAGE_SHIFT) >> 10; + list_for_each(p, &(zone->free_area[order].free_list[i])) { + if (size > *max_free) { + *max_free = size; + } + *total_free += size; + } + } + spin_unlock_irqrestore(&zone->lock, flags); + } +} + +struct profile_counter_pkt profile_builtin_stats[] = +{ + { + "Free memory(KB)", 0 + }, + { + "Max free Block(KB)", 0 + } +}; + +/* + * make a packet full of performance counters + */ +static char prof_pkt[PROFILE_MAX_PACKET_SIZE]; +static int profile_make_stats_packet(char *buf, int count) +{ + char *ptr = prof_pkt; + struct profile_header_counters hdr; + int stat_count = 0; + int i; + unsigned int total_free, max_free; + int builtin_count = sizeof(profile_builtin_stats) / sizeof(struct profile_counter_pkt); + + if (count > PROFILE_MAX_PACKET_SIZE) { + count = PROFILE_MAX_PACKET_SIZE; + } + stat_count = (count - sizeof(struct profile_header_counters)) / sizeof (struct profile_counter_pkt); + stat_count -= builtin_count; + + if (stat_count <= 0) { + return 0; + } + + if (stat_count > node->num_counters) { + stat_count = node->num_counters; + } + + hdr.magic = PROF_MAGIC_COUNTERS; + hdr.ultra_sample_time = node->clocks; + hdr.ultra_count = stat_count; + hdr.linux_sample_time = UBICOM32_IO_TIMER->sysval; + hdr.linux_count = builtin_count; + memcpy(ptr, (void *)&hdr, sizeof(struct profile_header_counters)); + ptr += sizeof(struct profile_header_counters); + + + for (i = 0; i < stat_count; ++i) { + memcpy(ptr, (void *)(&(node->counters[i])), sizeof(struct profile_counter)); + ptr += sizeof(struct profile_counter); + } + + /* + * built in statistics + */ + profile_get_memory_stats(&total_free, &max_free); + profile_builtin_stats[0].value = total_free; + profile_builtin_stats[1].value = max_free; + memcpy(ptr, (void *)profile_builtin_stats, sizeof(profile_builtin_stats)); + ptr += sizeof(profile_builtin_stats); + + if (copy_to_user(buf, prof_pkt, ptr - prof_pkt) != 0) { + return -EFAULT; + } + return ptr - prof_pkt; +} + +/* + * return a udp packet ready to send to the profiler tool + * when there are no packets left to make, return 0 + */ +static int profile_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) +{ + int result = 0; + if (!node) { + return -ENOENT; + } + node->busy = 1; + if (!node->enabled) { + node->busy = 0; + return -EPERM; + } + if (!node->samples) { + node->busy = 0; + return -ENOMEM; + } + + if (profile_first_packet) { + result = profile_make_stats_packet(buf, count); + profile_first_packet = 0; + } + if (result == 0) { + result = profile_make_data_packet(buf, count); + if (result == 0) { + profile_first_packet = 1; + } + } + node->busy = 0; + return result; + +} + +static int profile_release(struct inode *inode, struct file *filp) +{ + if (!node) { + return -ENOENT; + } + node->busy = 1; + if (node->enabled) { + node->enabled = 0; + node->count = 0; + node->tail = node->head; + node->busy = 0; + return 0; + } + node->busy = 0; + profile_first_packet = 1; + return -EBADF; +} + +static const struct file_operations profile_fops = { + .open = profile_open, + .read = profile_read, + .release = profile_release, +}; + +static int page_aligned(void *x) +{ + return !((unsigned int)x & ((1 << PAGE_SHIFT) - 1)); +} + +static int profile_maps_open(struct inode *inode, struct file *filp) +{ + struct rb_node *rb; + int num = 0; + int slab_start; + struct vm_area_struct *vma; + int type = PROFILE_MAP_TYPE_UNKNOWN; + int flags, i; + struct list_head *p; + struct zone *zone; + + /* + * get the slab data (first so dups will show up as vmas) + */ + slab_start = num; + num += kmem_cache_block_info("size-512", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + num += kmem_cache_block_info("size-1024", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + num += kmem_cache_block_info("size-2048", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + num += kmem_cache_block_info("size-4096", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + num += kmem_cache_block_info("size-8192", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + + for (i = slab_start; i < num; ++i) { + profile_pm[i].type_size |= PROFILE_MAP_TYPE_SMALL << PROFILE_MAP_TYPE_SHIFT; + } + + slab_start = num; + num += kmem_cache_block_info("dentry", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + num += kmem_cache_block_info("inode_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + num += kmem_cache_block_info("sysfs_dir_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + num += kmem_cache_block_info("proc_inode_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); + + for (i = slab_start; i < num; ++i) { + profile_pm[i].type_size |= PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT; + } + + /* + * get all the vma regions (allocated by mmap, most likely + */ +#if 0 + down_read(&nommu_vma_sem); + for (rb = rb_first(&nommu_vma_tree); rb && num < PROFILE_NUM_MAPS; rb = rb_next(rb)) { + vma = rb_entry(rb, struct vm_area_struct, vm_rb); + profile_pm[num].start = (vma->vm_start - SDRAMSTART) >> PAGE_SHIFT; + profile_pm[num].type_size = (vma->vm_end - vma->vm_start + (1 << PAGE_SHIFT) - 1) >> PAGE_SHIFT; + flags = vma->vm_flags & 0xf; + if (flags == (VM_READ | VM_EXEC)) { + type = PROFILE_MAP_TYPE_TEXT; + } else if (flags == (VM_READ | VM_WRITE | VM_EXEC)) { + type = PROFILE_MAP_TYPE_STACK; + } else if (flags == (VM_READ | VM_WRITE)) { + type = PROFILE_MAP_TYPE_APP_DATA; + } + profile_pm[num].type_size |= type << PROFILE_MAP_TYPE_SHIFT; + num++; + } + up_read(&nommu_vma_sem); + if (rb) { + return -ENOMEM; + } +#endif + + /* + * get all the free regions. In each zone, the array of free_area lists contains the first page of each frame of size 1 << order + */ + for_each_zone(zone) { + unsigned long order, flags, i; + struct page *page; + + if (!populated_zone(zone)) + continue; + + if (!is_normal(zone)) + continue; + + spin_lock_irqsave(&zone->lock, flags); + for_each_migratetype_order(order, i) { + list_for_each(p, &(zone->free_area[order].free_list[i])) { + page = list_entry(p, struct page, lru); + profile_pm[num].start = ((page_to_phys(page) - SDRAMSTART) >> PAGE_SHIFT) - 0x40; + profile_pm[num].type_size = (PROFILE_MAP_TYPE_FREE << PROFILE_MAP_TYPE_SHIFT) | order; + num++; + if (num >= PROFILE_NUM_MAPS) { + spin_unlock_irqrestore(&zone->lock, flags); + return -ENOMEM; + } + } + } + spin_unlock_irqrestore(&zone->lock, flags); + } + + /* + * get the filesystem inodes + */ + list_for_each(p, &(super_blocks)) { + struct super_block *sb; + struct list_head *q; + if (num >= PROFILE_NUM_MAPS) + break; + sb = list_entry(p, struct super_block, s_list); + if (page_aligned(sb)) { + profile_pm[num].start = ((unsigned int)sb - SDRAMSTART) >> PAGE_SHIFT; + profile_pm[num].type_size = (PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT); + num++; + } + list_for_each(q, &(sb->s_inodes)) { + struct inode *in; + if (num >= PROFILE_NUM_MAPS) + break; + in = list_entry(q, struct inode, i_sb_list); + if (page_aligned(in)) { + profile_pm[num].start = ((unsigned int)in - SDRAMSTART) >> PAGE_SHIFT; + profile_pm[num].type_size = (PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT); + num++; + } + } + } + + /* + * get the buffer cache pages + */ + for (i = 0; i < num_physpages && num < PROFILE_NUM_MAPS; ++i) { + if ((mem_map + i)->flags & (1 << PG_lru)) { + int start = i; + while ((mem_map + i)->flags & (1 << PG_lru) && i < num_physpages) + i++; + profile_pm[num].start = start; + profile_pm[num].type_size = (i - start) | (PROFILE_MAP_TYPE_CACHE << PROFILE_MAP_TYPE_SHIFT); + num++; + } + } + + filp->private_data = (void *)num; + return 0; +} + +/* + * return one packet of map data, or 0 if all maps have been returned already + */ +static int profile_maps_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) +{ + struct profile_header_maps header; + char *p = buf + sizeof(header); + int total = (int)filp->private_data; + + header.count = (count - sizeof(header)) / sizeof(struct profile_map); + if (header.count > PROFILE_MAX_MAPS) { + header.count = PROFILE_MAX_MAPS;; + } + if (header.count > total - *f_pos) { + header.count = total - *f_pos; + } + + if (header.count == 0) { + return 0; + } + + header.magic = PROF_MAGIC_MAPS; + header.page_shift = PAGE_SHIFT; + + if (copy_to_user(buf, &header, sizeof(header)) != 0) { + return -EFAULT; + } + if (copy_to_user(p, (void *)&profile_pm[*f_pos], sizeof(struct profile_map) * header.count) != 0) { + return -EFAULT; + } + *f_pos += header.count; + + return sizeof(header) + sizeof(struct profile_map) * header.count; +} + +static int profile_maps_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static const struct file_operations profile_maps_fops = { + .open = profile_maps_open, + .read = profile_maps_read, + .release = profile_maps_release, +}; + +static int profile_rate_show(struct seq_file *m, void *v) +{ + if (node) { + seq_printf(m, "%d samples per second. %d virtual counters.\n", node->rate, node->num_counters); + } else { + seq_printf(m, "Profiler is not initialized.\n"); + } + return 0; +} + +static int profile_rate_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, profile_rate_show, NULL); +} + +static int profile_rate_write(struct file *filp, const char *buf, size_t len, loff_t *off) +{ + *off = 0; + return 0; +} + +static const struct file_operations profile_rate_fops = { + .open = profile_rate_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = profile_rate_write, +}; + +int ubi32_profile_init_module(void) +{ + struct proc_dir_entry *pdir; + + /* + * find the device + */ + node = (struct profilenode *)devtree_find_node("profiler"); + if (!node) { + printk(KERN_INFO "Profiler does not exist.\n"); + return -ENODEV; + } + + /* + * allocate the sample buffer + */ + node->max_samples = PROFILE_MAX_SAMPLES; + node->samples = kmalloc(node->max_samples * sizeof(struct profile_sample), GFP_KERNEL); + if (!node->samples) { + printk(KERN_INFO "Profiler sample buffer kmalloc failed.\n"); + return -ENOMEM; + } + + /* + * connect to the file system + */ + pdir = proc_mkdir("profile", NULL); + if (!pdir) { + return -ENOMEM; + } + if (!proc_create("data", 0, pdir, &profile_fops)) { + return -ENOMEM; + } + if (!proc_create("rate", 0, pdir, &profile_rate_fops)) { + return -ENOMEM; + } + if (!proc_create("maps", 0, pdir, &profile_maps_fops)) { + return -ENOMEM; + } + return 0; +} + + +module_init(ubi32_profile_init_module); + +MODULE_AUTHOR("David Fotland"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/profile.h b/target/linux/ubicom32/files/arch/ubicom32/mach-common/profile.h new file mode 100644 index 0000000000..e6ff7d9fe0 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/profile.h @@ -0,0 +1,82 @@ +/* + * arch/ubicom32/mach-common/profile.h + * Private data for the profile module + * + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include "profpkt.h" + +#ifndef _PROFILE_H_ +#define _PROFILE_H_ + +#define PROFILE_MAX_THREADS 16 +#define PROFILE_MAX_SAMPLES 1024 + +struct profile_sample; +struct oprofile_sample; + +/* + * values chosen so all counter values fit in a single UDP packet + */ +#define PROFILE_NODE_MAX_COUNTERS 32 + +struct profile_counter { + char name[PROFILE_COUNTER_NAME_LENGTH]; + unsigned int value; +}; + +struct profilenode { + struct devtree_node dn; + volatile u32_t enabled; /* Is the profiler enabled to take samples? */ + volatile u32_t busy; /* set when the samples are being read by the driver */ + volatile u32_t rate; /* What is the sampling rate? */ + volatile u32_t enabled_threads; /* which threads were enabled at the last sample time */ + volatile u32_t hrt; /* HRT threads */ + volatile u32_t profiler_thread; /* thread running the profile sampler */ + volatile u32_t clocks; /* system clock timer at last sample */ + volatile u32_t clock_freq; /* clock frequency in Hz */ + volatile u32_t ddr_freq; /* memory frequency */ + volatile u32_t cpu_id; /* chip_id register */ + volatile u32_t inst_count[PROFILE_MAX_THREADS]; /* sampled instruction counts at most recent sample */ + volatile u32_t stats[4]; /* contents of the cache statistics counters */ + volatile u16_t head; /* sample taker puts samples here */ + volatile u16_t tail; /* packet filler takes samples here */ + volatile u16_t count; /* number of valid samples */ + volatile u16_t max_samples; /* how many samples can be in the samples array */ + struct profile_sample *samples; /* samples array allocated by the linux driver */ + volatile u32_t num_counters; /* how many registered performance counters */ + volatile struct profile_counter counters[PROFILE_NODE_MAX_COUNTERS]; + + /* unimplemented interface for future oprofile work */ + volatile u16_t oprofile_head; /* sample taker puts samples here */ + volatile u16_t oprofile_tail; /* packet filler takes samples here */ + volatile u16_t oprofile_count; /* how many oprofile sampels are are in use */ + volatile u16_t oprofile_max_samples; /* samples array size for oprofile samples */ + struct oprofile_sample *oprofile_samples; /* oprofile sample buffer */ +}; + +#endif diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/profpkt.h b/target/linux/ubicom32/files/arch/ubicom32/mach-common/profpkt.h new file mode 100644 index 0000000000..37d9219a6d --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/profpkt.h @@ -0,0 +1,158 @@ + +/* + * arch/ubicom32/mach-common/profpkt.c + * Ubicom32 Profiler packet formats for communication between the linux proc driver and the profiler display tool + * + * (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 . + */ + +#define PROFILE_PORT 51080 +#define PROFILE_POSIX_NAME_LENGTH 32 + +/* + * profile UDP packet format for communicating between ip3k and host + * + * every packet starts with a header, followed by samples. + * samples are only taken for non-hrt threads that are + * active + */ +#define PROF_MAGIC 0x3ea0 +#define PROF_MAGIC_COUNTERS 0x9ea0 +#define PROF_MAGIC_MAPS 0xaea0 + +/* + * Versions (31 max): + * 1 to 4 were before 6.0 release, development versions + * 5 was forward compatible version, shipped with 6.0 and 6.1 + * 6 adds heap packets, and clock_freq to header, shipped with 6.2 + * 7 adds a sequence numbers to check for dropped packets, shipped with 6.3.5 + * 8 adds mqueue timing information, shipped with 6.3.5 + * 9 adds sdram heap size information, shipped with 6.4 + * 10 adds heapmem heap callers and long latency stack traces. shipped with 6.4 + * 11 adds support for Mars (IP5K). shipped with 6.10 + * 12 adds more support for Mars. Shipped with 7.0 + * 13 adds per sample latency measurement. Shipped with 7.2 + * 14 changes the heap format and adds a string packet. Shipped with 7.4 + * 15 adds dsr stats and posix. shipped with 7.6 + * 16 corrects maximum packet count for Ares. ships with 7.9 + * 17 adds a5 register value to sample + */ + +#define PROFILE_VERSION 17 +#define PROFILE_MAX_PACKET_SIZE 1440 + +#define PROFILE_MAX_THREADS 16 + +/* + * each packet starts with a profile_header, then sample_count samples + * samples are gprof samples of pc, the return address, condition codes, and + * active threads + */ +struct profile_header { + u16_t magic; /* magic number and version */ + u8_t header_size; /* number of bytes in profile header */ + u8_t sample_count; /* number of samples in the packet */ + u32_t clocks; /* clock counter value */ + u32_t instruction_count[PROFILE_MAX_THREADS]; + /* instructions executed per thread */ + u32_t profile_instructions; /* instructions executed by profiler mainline */ + u16_t enabled; /* which threads are enabled */ + u16_t hrt; /* which threads are hrt */ + u16_t high; /* which threads are high priority */ + u16_t profiler_thread; /* which thread runs the profiler */ + u32_t heap_free; /* current free on-cihp heap space in bytes */ + u32_t heap_low_water; /* on-chip heap low water mark */ + u32_t netpage_free; /* number of free on-chip net pages */ + u32_t netpage_low_water; /* low water mark on free on-chip netpages */ + u32_t min_sp[PROFILE_MAX_THREADS]; + /* stack pointer values per thread */ + u32_t clock_freq; /* clock frequency (Hz) of system being analyzed */ + u32_t seq_num; /* to detect dropped profiler packets */ + u32_t timing_sequence; /* sample number since boot */ + u32_t timing_interval; /* second per sample timing interval */ + u32_t timing_worst_time; /* duration of longest finction called, in core clocks */ + u32_t timing_function; /* address of longest function */ + u32_t timing_average; /* average time of all functions in last interval */ + u32_t timing_count; /* number of functions called in last interval */ + u32_t extheap_free; /* current free extmem heap space in bytes */ + u32_t extheap_low_water; /* extmem heap low water mark */ + u32_t cpu_id; /* CHIP_ID register contents */ + u32_t perf_counters[4]; /* contents of the CPU performance counters */ + u8_t perf_config[4]; /* what is being counted */ + u32_t ddr_freq; /* DDR clock frequency */ + u32_t extnetpage_free; /* number of free off chip net pages */ + u32_t extnetpage_low_water; /* low water mark on off-chip free netpages */ + u32_t dsr_max_latency; /* max time to process a dsr interrupt, in clocks, since last packet */ + u32_t dsr_ave_latency; /* average dsr latency over last DSR_STATS_RECENT_COUNT interrupts */ + u32_t dsr_count; /* number of dsr interrupts since last packet */ +}; + +struct profile_header_counters { + u16_t magic; + u16_t ultra_count; /* how many ultra counters follow this */ + u32_t ultra_sample_time; /* in chip clocks */ + u32_t linux_count; /* how many linux counters follow this */ + u32_t linux_sample_time; +}; + +/* + * values chosen so all counter values fit in a single 1400 byte UDP packet + */ +#define PROFILE_COUNTER_NAME_LENGTH 20 +#define PROFILE_MAX_COUNTERS ((PROFILE_MAX_PACKET_SIZE - sizeof(struct profile_header_counters)) / (PROFILE_COUNTER_NAME_LENGTH + 4)) + +struct profile_counter_pkt { + char name[PROFILE_COUNTER_NAME_LENGTH]; + unsigned int value; +}; + +/* + * send memory maps from linux to profiler tool + */ + +struct profile_header_maps { + u16_t magic; + u16_t count; + u32_t page_shift; +}; + +#define PROFILE_MAP_NUM_TYPES 32 + +/* types 0-15: size field is order. True size is 2^order */ +#define PROFILE_MAP_TYPE_UNKNOWN 0 +#define PROFILE_MAP_TYPE_FREE 1 +#define PROFILE_MAP_TYPE_SMALL 2 +#define PROFILE_MAP_TYPE_FS 3 +/* types 16-31: size field is pages. True size is (1 << PAGE_SHIFT) * size */ +#define PROFILE_MAP_SIZE_TYPE 16 +#define PROFILE_MAP_TYPE_TEXT 16 +#define PROFILE_MAP_TYPE_STACK 17 +#define PROFILE_MAP_TYPE_APP_DATA 18 +#define PROFILE_MAP_TYPE_CACHE 19 +#define PROFILE_MAP_RESERVED 24 + +#define PROFILE_MAP_TYPE_SHIFT 11 +#define PROFILE_MAP_SIZE_MASK 0x7ff + +struct profile_map { + u16_t start; /* start page number of segment, relative to start of DRAM */ + u16_t type_size; /* type (4 bits) of the segment and size in pages (12 bits) */ +}; + +#define PROFILE_MAX_MAPS (PROFILE_MAX_PACKET_SIZE - sizeof(struct profile_header_maps)) / sizeof(struct profile_map) diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/ring_tio.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ring_tio.c new file mode 100644 index 0000000000..9d0f8cd2a6 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + */ + +#include +#include + +#include +#include + +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); + } +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-bcm539x-reg.h b/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-bcm539x-reg.h new file mode 100644 index 0000000000..ca64eb1465 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-bcm539x-reg.h @@ -0,0 +1,221 @@ +/* + * arch/ubicom32/mach-common/switch-bcm539x-reg.h + * Broadcom switch 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +/* + * Broadcom 53xx RoboSwitch device driver. + * + * Copyright 2007, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#ifndef _SWITCH_BCM539X_REG_H_ +#define _SWITCH_BCM539X_REG_H_ + +#define BCM539X_CMD_READ 0x60 +#define BCM539X_CMD_WRITE 0x61 + +#define BCM539X_GLOBAL_SPI_DATA0 0xf0 + +#define BCM539X_GLOBAL_SPI_STATUS 0xfe +#define BCM539X_GLOBAL_SPI_ST_SPIF (1<<7) +#define BCM539X_GLOBAL_SPI_ST_RACK (1<<5) + +#define BCM539X_GLOBAL_PAGE 0xff + +#define PAGE_PORT_TC 0x00 // Port Traffic Control Register + +#define PAGE_QOS_CTL 0x30 // QoS Global Control Register +#define PAGE_QOS_TAG 0x34 // Default IEEE 802.1Q TAG Register + +#define PAGE_MII_CTL_PORT0 0x10 // Internal PHY MII Register +#define PAGE_MII_CTL_PORT1 0x11 +#define PAGE_MII_CTL_PORT2 0x12 +#define PAGE_MII_CTL_PORT3 0x13 +#define PAGE_MII_CTL_PORT4 0x14 + +#define PAGE_STATUS 0x01 // Status Register Page +#define PAGE_RATE_CONTROL 0x41 // Broadcast Storm Suppression Register + +#define REG_GRATE_CONTROL 0x00 + +#define REG_LED_POWER 0x12 + +// Ingress Rate Control +#define REG_IRATE_CONTROLP0 0x10 +#define REG_IRATE_CONTROLP1 0x14 +#define REG_IRATE_CONTROLP2 0x18 +#define REG_IRATE_CONTROLP3 0x1C +#define REG_IRATE_CONTROLP4 0x20 +#define REG_IRATE_CONTROLP7 0x2C +#define REG_IRATE_CONTROLPI 0x30 + +// Egress Rate Control +#define REG_ERATE_CONTROLP0 0x80 +#define REG_ERATE_CONTROLP1 0x82 +#define REG_ERATE_CONTROLP2 0x84 +#define REG_ERATE_CONTROLP3 0x86 +#define REG_ERATE_CONTROLP4 0x88 +#define REG_ERATE_CONTROLP5 0x8A +#define REG_ERATE_CONTROLP6 0x8C +#define REG_ERATE_CONTROLP7 0x8E +#define REG_ERATE_CONTROLPI 0x90 + +#define REG_LINK_STATUS 0x00 + +#define REG_TC_PORT0 0x00 +#define REG_TC_PORT1 0x01 +#define REG_TC_PORT2 0x02 +#define REG_TC_PORT3 0x03 +#define REG_TC_PORT4 0x04 +#define REG_TC_PORT5 0x05 + +#define REG_SPEED_CTL 0x00 +#define REG_SPEED_ADV100 0x08 +#define REG_SPEED_ADV1000 0x12 + +#define REG_QOS_EN 0x00 +#define REG_QOS_TAG_PORT1 0x12 // Default IEEE 802.1Q TAG, PORT 1 +#define REG_QOS_TAG_PORT2 0x14 // Default IEEE 802.1Q TAG, PORT 2 +#define REG_QOS_TAG_PORT3 0x16 // Default IEEE 802.1Q TAG, PORT 3 +#define REG_QOS_TAG_PORT4 0x18 // Default IEEE 802.1Q TAG, PORT 4 +#define REG_QOS_PID_PORT1 0x52 // Ingress Port Priority ID MAP, PORT 1 +#define REG_QOS_PID_PORT2 0x54 // Ingress Port Priority ID MAP, PORT 2 +#define REG_QOS_PID_PORT3 0x56 // Ingress Port Priority ID MAP, PORT 3 +#define REG_QOS_PID_PORT4 0x58 // Ingress Port Priority ID MAP, PORT 4 +#define REG_QOS_TXQ_CTL 0x80 // Tx Queue Control Register +#define REG_QOS_TXQ_WHTQ0 0x81 // Tx Queue Weight Register Queue 0 +#define REG_QOS_TXQ_WHTQ1 0x82 // Tx Queue Weight Register Queue 1 +#define REG_QOS_TXQ_WHTQ2 0x83 // Tx Queue Weight Register Queue 2 +#define REG_QOS_TXQ_WHTQ3 0x84 // Tx Queue Weight Register Queue 3 + +#define REG_CTRL_PPSEL 0x24 /* 5397: Protected port select register */ + +#define RATE_CONTROL_ENABLED (1 << 22) +#define RATE_CONTROL_BSIZE ((1 << 10) | (1 << 9) | (1 << 8)) + +#define RATE_CONTROL_HIGH ((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4)) +#define RATE_CONTROL_HIGH_N ~((1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)) + +#define RATE_CONTROL_MEDIUM ((1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)) +#define RATE_CONTROL_MEDIUM_N ~((1 << 7)) + +#define RATE_CONTROL_NORMAL ((1 << 5) | (1 << 2) | (1 << 0)) +#define RATE_CONTROL_NORMAL_N ~((1 << 7) | (1 << 6) | (1 << 4) | (1 << 3) | (1 << 1)) + +#define RATE_CONTROL_LOW ((1 << 4) | (1 << 3) | (1 << 0)) +#define RATE_CONTROL_LOW_N ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2) | (1 << 1)) + +// --- Gemtek, Configure the switch to support Ethernet Port QoS + +/* MII access registers */ +#define PSEUDO_PHYAD 0x1E /* MII Pseudo PHY address */ +#define REG_MII_PAGE 0x10 /* MII Page register */ +#define REG_MII_ADDR 0x11 /* MII Address register */ +#define REG_MII_DATA0 0x18 /* MII Data register 0 */ +#define REG_MII_DATA1 0x19 /* MII Data register 1 */ +#define REG_MII_DATA2 0x1a /* MII Data register 2 */ +#define REG_MII_DATA3 0x1b /* MII Data register 3 */ + +/* Page numbers */ +#define PAGE_CTRL 0x00 /* Control page */ +#define PAGE_MMR 0x02 /* 5397 Management/Mirroring page */ +#define PAGE_VTBL 0x05 /* ARL/VLAN Table access page */ +#define PAGE_VLAN 0x34 /* VLAN page */ + +/* Control page registers */ +#define REG_CTRL_PORT0 0x00 /* Port 0 traffic control register */ +#define REG_CTRL_PORT1 0x01 /* Port 1 traffic control register */ +#define REG_CTRL_PORT2 0x02 /* Port 2 traffic control register */ +#define REG_CTRL_PORT3 0x03 /* Port 3 traffic control register */ +#define REG_CTRL_PORT4 0x04 /* Port 4 traffic control register */ +#define REG_CTRL_PORT5 0x05 /* Port 5 traffic control register */ +#define REG_CTRL_PORT6 0x06 /* Port 6 traffic control register */ +#define REG_CTRL_PORT7 0x07 /* Port 7 traffic control register */ +#define REG_CTRL_MODE 0x0B /* Switch Mode register */ +#define REG_CTRL_MIIPO 0x0E /* 5325: MII Port Override register */ +#define REG_CTRL_SRST 0x79 /* Software reset control register */ + +#define REG_DEVICE_ID 0x30 /* 539x Device id: */ +#define DEVID5395 0x95 /* 5395 */ +#define DEVID5397 0x97 /* 5397 */ +#define DEVID5398 0x98 /* 5398 */ +#define REG_REVISION_ID 0x40 /* 539x Revision id: */ + +/* VLAN page registers */ +#define REG_VLAN_CTRL0 0x00 /* VLAN Control 0 register */ +#define REG_VLAN_CTRL1 0x01 /* VLAN Control 1 register */ +#define REG_VLAN_CTRL2 0x02 /* VLAN Control 2 register */ +#define REG_VLAN_CTRL3 0x03 /* VLAN Control 3 register */ +#define REG_VLAN_CTRL4 0x04 /* VLAN Control 4 register */ +#define REG_VLAN_CTRL5 0x05 /* VLAN Control 5 register */ +#define REG_VLAN_ACCESS 0x06 /* VLAN Table Access register */ +#define REG_VLAN_WRITE 0x08 /* VLAN Write register */ +#define REG_VLAN_READ 0x0C /* VLAN Read register */ +#define REG_VLAN_PTAG0 0x10 /* VLAN Default Port Tag register - port 0 */ +#define REG_VLAN_PTAG1 0x12 /* VLAN Default Port Tag register - port 1 */ +#define REG_VLAN_PTAG2 0x14 /* VLAN Default Port Tag register - port 2 */ +#define REG_VLAN_PTAG3 0x16 /* VLAN Default Port Tag register - port 3 */ +#define REG_VLAN_PTAG4 0x18 /* VLAN Default Port Tag register - port 4 */ +#define REG_VLAN_PTAG5 0x1a /* VLAN Default Port Tag register - port 5 */ +#define REG_VLAN_PTAG6 0x1c /* VLAN Default Port Tag register - port 6 */ +#define REG_VLAN_PTAG7 0x1e /* VLAN Default Port Tag register - port 7 */ +#define REG_VLAN_PTAG8 0x20 /* 539x: VLAN Default Port Tag register - IMP port */ +#define REG_VLAN_PMAP 0x20 /* 5325: VLAN Priority Re-map register */ + +/* ARL/VLAN Table Access page registers */ +#define REG_VTBL_CTRL 0x00 /* ARL Read/Write Control */ +#define REG_VTBL_MINDX 0x02 /* MAC Address Index */ +#define REG_VTBL_VINDX 0x08 /* VID Table Index */ +#define REG_VTBL_ARL_E0 0x10 /* ARL Entry 0 */ +#define REG_VTBL_ARL_E1 0x18 /* ARL Entry 1 */ +#define REG_VTBL_DAT_E0 0x18 /* ARL Table Data Entry 0 */ +#define REG_VTBL_SCTRL 0x20 /* ARL Search Control */ +#define REG_VTBL_SADDR 0x22 /* ARL Search Address */ +#define REG_VTBL_SRES 0x24 /* ARL Search Result */ +#define REG_VTBL_SREXT 0x2c /* ARL Search Result */ +#define REG_VTBL_VID_E0 0x30 /* VID Entry 0 */ +#define REG_VTBL_VID_E1 0x32 /* VID Entry 1 */ +#define REG_VTBL_PREG 0xFF /* Page Register */ +#define REG_VTBL_ACCESS 0x60 /* VLAN table access register */ +#define REG_VTBL_INDX 0x61 /* VLAN table address index register */ +#define REG_VTBL_ENTRY 0x63 /* VLAN table entry register */ +#define REG_VTBL_ACCESS_5395 0x80 /* VLAN table access register */ +#define REG_VTBL_INDX_5395 0x81 /* VLAN table address index register */ +#define REG_VTBL_ENTRY_5395 0x83 /* VLAN table entry register */ + +/* SPI registers */ +#define REG_SPI_PAGE 0xff /* SPI Page register */ + +#endif diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-bcm539x.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-bcm539x.c new file mode 100644 index 0000000000..d9eca381a5 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-bcm539x.c @@ -0,0 +1,1195 @@ +/* + * arch/ubicom32/mach-common/switch-bcm539x.c + * BCM539X switch driver, SPI mode + * + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "switch-core.h" +#include "switch-bcm539x-reg.h" + +#define DRIVER_NAME "bcm539x-spi" +#define DRIVER_VERSION "1.0" + +#undef BCM539X_DEBUG +#define BCM539X_SPI_RETRIES 100 + +struct bcm539x_data { + struct switch_device *switch_dev; + + /* + * Our private data + */ + struct spi_device *spi; + struct switch_core_platform_data *pdata; + + /* + * Last page we accessed + */ + u8_t last_page; + + /* + * 539x Device ID + */ + u8_t device_id; +}; + +/* + * bcm539x_wait_status + * Waits for the specified bit in the status register to be set/cleared. + */ +static int bcm539x_wait_status(struct bcm539x_data *bd, u8_t mask, int set) +{ + u8_t txbuf[2]; + u8_t rxbuf; + int i; + int ret; + + txbuf[0] = BCM539X_CMD_READ; + txbuf[1] = BCM539X_GLOBAL_SPI_STATUS; + for (i = 0; i < BCM539X_SPI_RETRIES; i++) { + ret = spi_write_then_read(bd->spi, txbuf, 2, &rxbuf, 1); + rxbuf &= mask; + if ((set && rxbuf) || (!set && !rxbuf)) { + return 0; + } + udelay(1); + } + + return -EIO; +} + +/* + * bcm539x_set_page + * Sets the register page for access (only if necessary) + */ +static int bcm539x_set_page(struct bcm539x_data *bd, u8_t page) +{ + u8_t txbuf[3]; + + if (page == bd->last_page) { + return 0; + } + + bd->last_page = page; + + txbuf[0] = BCM539X_CMD_WRITE; + txbuf[1] = BCM539X_GLOBAL_PAGE; + txbuf[2] = page; + + return spi_write(bd->spi, txbuf, 3); +} + +/* + * bcm539x_write_bytes + * Writes a number of bytes to a given page and register + */ +static int bcm539x_write_bytes(struct bcm539x_data *bd, u8_t page, + u8_t reg, void *buf, u8_t len) +{ + int ret; + u8_t *txbuf; + + txbuf = kmalloc(2 + len, GFP_KERNEL); + if (!txbuf) { + return -ENOMEM; + } + + /* + * Make sure the chip has finished processing our previous request + */ + ret = bcm539x_wait_status(bd, BCM539X_GLOBAL_SPI_ST_SPIF, 0); + if (ret) { + goto done; + } + + /* + * Set the page + */ + ret = bcm539x_set_page(bd, page); + if (ret) { + goto done; + } + + /* + * Read the data + */ + txbuf[0] = BCM539X_CMD_WRITE; + txbuf[1] = reg; + memcpy(&txbuf[2], buf, len); + +#ifdef BCM539X_DEBUG + { + int i; + printk("write page %02x reg %02x len=%d buf=", page, reg, len); + for (i = 0; i < len + 2; i++) { + printk("%02x ", txbuf[i]); + } + printk("\n"); + } +#endif + + ret = spi_write(bd->spi, txbuf, 2 + len); + +done: + kfree(txbuf); + return ret; +} + +/* + * bcm539x_write_32 + * Writes 32 bits of data to the given page and register + */ +static inline int bcm539x_write_32(struct bcm539x_data *bd, u8_t page, + u8_t reg, u32_t data) +{ + data = cpu_to_le32(data); + return bcm539x_write_bytes(bd, page, reg, &data, 4); +} + +/* + * bcm539x_write_16 + * Writes 16 bits of data to the given page and register + */ +static inline int bcm539x_write_16(struct bcm539x_data *bd, u8_t page, + u8_t reg, u16_t data) +{ + data = cpu_to_le16(data); + return bcm539x_write_bytes(bd, page, reg, &data, 2); +} + +/* + * bcm539x_write_8 + * Writes 8 bits of data to the given page and register + */ +static inline int bcm539x_write_8(struct bcm539x_data *bd, u8_t page, + u8_t reg, u8_t data) +{ + return bcm539x_write_bytes(bd, page, reg, &data, 1); +} + +/* + * bcm539x_read_bytes + * Reads a number of bytes from a given page and register + */ +static int bcm539x_read_bytes(struct bcm539x_data *bd, u8_t page, + u8_t reg, void *buf, u8_t len) +{ + u8_t txbuf[2]; + int ret; + + /* + * (1) Make sure the chip has finished processing our previous request + */ + ret = bcm539x_wait_status(bd, BCM539X_GLOBAL_SPI_ST_SPIF, 0); + if (ret) { + return ret; + } + + /* + * (2) Set the page + */ + ret = bcm539x_set_page(bd, page); + if (ret) { + return ret; + } + + /* + * (3) Kick off the register read + */ + txbuf[0] = BCM539X_CMD_READ; + txbuf[1] = reg; + ret = spi_write_then_read(bd->spi, txbuf, 2, txbuf, 1); + if (ret) { + return ret; + } + + /* + * (4) Wait for RACK + */ + ret = bcm539x_wait_status(bd, BCM539X_GLOBAL_SPI_ST_RACK, 1); + if (ret) { + return ret; + } + + /* + * (5) Read the data + */ + txbuf[0] = BCM539X_CMD_READ; + txbuf[1] = BCM539X_GLOBAL_SPI_DATA0; + + ret = spi_write_then_read(bd->spi, txbuf, 2, buf, len); + +#ifdef BCM539X_DEBUG + { + int i; + printk("read page %02x reg %02x len=%d rxbuf=", + page, reg, len); + for (i = 0; i < len; i++) { + printk("%02x ", ((u8_t *)buf)[i]); + } + printk("\n"); + } +#endif + + return ret; +} + +/* + * bcm539x_read_32 + * Reads an 32 bit number from a given page and register + */ +static int bcm539x_read_32(struct bcm539x_data *bd, u8_t page, + u8_t reg, u32_t *buf) +{ + int ret = bcm539x_read_bytes(bd, page, reg, buf, 4); + *buf = le32_to_cpu(*buf); + return ret; +} + +/* + * bcm539x_read_16 + * Reads an 16 bit number from a given page and register + */ +static int bcm539x_read_16(struct bcm539x_data *bd, u8_t page, + u8_t reg, u16_t *buf) +{ + int ret = bcm539x_read_bytes(bd, page, reg, buf, 2); + *buf = le16_to_cpu(*buf); + return ret; +} + +/* + * bcm539x_read_8 + * Reads an 8 bit number from a given page and register + */ +static int bcm539x_read_8(struct bcm539x_data *bd, u8_t page, + u8_t reg, u8_t *buf) +{ + return bcm539x_read_bytes(bd, page, reg, buf, 1); +} + +/* + * bcm539x_set_mode + */ +static int bcm539x_set_mode(struct bcm539x_data *bd, int state) +{ + u8_t buf; + int ret; + + ret = bcm539x_read_8(bd, PAGE_PORT_TC, REG_CTRL_MODE, &buf); + if (ret) { + return ret; + } + + buf &= ~(1 << 1); + buf |= state ? (1 << 1) : 0; + + ret = bcm539x_write_8(bd, PAGE_PORT_TC, REG_CTRL_MODE, buf); + return ret; +} + +/* + * bcm539x_handle_reset + */ +static int bcm539x_handle_reset(struct switch_device *dev, char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + int ret; + + ret = bcm539x_write_8(bd, PAGE_PORT_TC, REG_CTRL_SRST, + (1 << 7) | (1 << 4)); + if (ret) { + return ret; + } + + udelay(20); + + ret = bcm539x_write_8(bd, PAGE_PORT_TC, REG_CTRL_SRST, 0); + return ret; +} + +/* + * bcm539x_handle_vlan_ports_read + */ +static int bcm539x_handle_vlan_ports_read(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + int j; + int len = 0; + u8_t rxbuf8; + u32_t rxbuf32; + int ret; + + ret = bcm539x_write_16(bd, PAGE_VTBL, REG_VTBL_INDX_5395, inst); + if (ret) { + return ret; + } + + ret = bcm539x_write_8(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, + (1 << 7) | 1); + if (ret) { + return ret; + } + + /* + * Wait for completion + */ + for (j = 0; j < BCM539X_SPI_RETRIES; j++) { + ret = bcm539x_read_8(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, + &rxbuf8); + if (ret) { + return ret; + } + if (!(rxbuf8 & (1 << 7))) { + break; + } + } + + if (j == BCM539X_SPI_RETRIES) { + return -EIO; + } + + /* + * Read the table entry + */ + ret = bcm539x_read_32(bd, PAGE_VTBL, REG_VTBL_ENTRY_5395, &rxbuf32); + if (ret) { + return ret; + } + + for (j = 0; j < 9; j++) { + if (rxbuf32 & (1 << j)) { + u16_t rxbuf16; + len += sprintf(buf + len, "%d", j); + if (rxbuf32 & (1 << (j + 9))) { + buf[len++] = 'u'; + } else { + buf[len++] = 't'; + } + ret = bcm539x_read_16(bd, PAGE_VLAN, + REG_VLAN_PTAG0 + (j << 1), + &rxbuf16); + if (ret) { + return ret; + } + if (rxbuf16 == inst) { + buf[len++] = '*'; + } + buf[len++] = '\t'; + } + } + + len += sprintf(buf + len, "\n"); + buf[len] = '\0'; + + return len; +} + +/* + * bcm539x_handle_vlan_ports_write + */ +static int bcm539x_handle_vlan_ports_write(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + int j; + u32_t untag; + u32_t ports; + u32_t def; + + u8_t rxbuf8; + u16_t rxbuf16; + int ret; + + switch_parse_vlan_ports(dev, buf, &untag, &ports, &def); + +#ifdef BCM539X_DEBUG + printk(KERN_DEBUG "'%s' inst=%d untag=%08x ports=%08x def=%08x\n", + buf, inst, untag, ports, def); +#endif + + if (!ports) { + return 0; + } + + /* + * Change default vlan tag + */ + for (j = 0; j < 9; j++) { + if ((untag | def) & (1 << j)) { + ret = bcm539x_write_16(bd, PAGE_VLAN, + REG_VLAN_PTAG0 + (j << 1), + inst); + if (ret) { + return ret; + } + continue; + } + + if (!(dev->port_mask[0] & (1 << j))) { + continue; + } + + /* + * Remove any ports which are not listed anymore as members of + * this vlan + */ + ret = bcm539x_read_16(bd, PAGE_VLAN, + REG_VLAN_PTAG0 + (j << 1), &rxbuf16); + if (ret) { + return ret; + } + if (rxbuf16 == inst) { + ret = bcm539x_write_16(bd, PAGE_VLAN, + REG_VLAN_PTAG0 + (j << 1), 0); + if (ret) { + return ret; + } + } + } + + /* + * Write the VLAN table + */ + ret = bcm539x_write_16(bd, PAGE_VTBL, REG_VTBL_INDX_5395, inst); + if (ret) { + return ret; + } + + ret = bcm539x_write_32(bd, PAGE_VTBL, REG_VTBL_ENTRY_5395, + (untag << 9) | ports); + if (ret) { + return ret; + } + + ret = bcm539x_write_8(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, + (1 << 7) | 0); + if (ret) { + return ret; + } + + /* + * Wait for completion + */ + for (j = 0; j < BCM539X_SPI_RETRIES; j++) { + ret = bcm539x_read_bytes(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, + &rxbuf8, 1); + if (ret) { + return ret; + } + if (!(rxbuf8 & (1 << 7))) { + break; + } + } + + return (j < BCM539X_SPI_RETRIES) ? 0 : -EIO; +} + +/* + * Handlers for /vlan/ + */ +static const struct switch_handler bcm539x_switch_handlers_vlan_dir[] = { + { + .name = "ports", + .read = bcm539x_handle_vlan_ports_read, + .write = bcm539x_handle_vlan_ports_write, + }, + { + }, +}; + +/* + * bcm539x_handle_vlan_delete_write + */ +static int bcm539x_handle_vlan_delete_write(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + int vid; + u8_t rxbuf8; + u32_t txbuf; + int j; + int ret; + + vid = simple_strtoul(buf, NULL, 0); + if (!vid) { + return -EINVAL; + } + + /* + * Disable this VLAN + * + * Go through the port-based vlan registers and clear the appropriate + * ones out + */ + for (j = 0; j < 9; j++) { + u16_t rxbuf16; + ret = bcm539x_read_16(bd, PAGE_VLAN, REG_VLAN_PTAG0 + (j << 1), + &rxbuf16); + if (ret) { + return ret; + } + if (rxbuf16 == vid) { + txbuf = 0; + ret = bcm539x_write_16(bd, PAGE_VLAN, + REG_VLAN_PTAG0 + (j << 1), + txbuf); + if (ret) { + return ret; + } + } + } + + /* + * Write the VLAN table + */ + txbuf = vid; + ret = bcm539x_write_16(bd, PAGE_VTBL, REG_VTBL_INDX_5395, txbuf); + if (ret) { + return ret; + } + + txbuf = 0; + ret = bcm539x_write_32(bd, PAGE_VTBL, REG_VTBL_ENTRY_5395, txbuf); + if (ret) { + return ret; + } + + txbuf = (1 << 7) | (0); + ret = bcm539x_write_8(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, txbuf); + if (ret) { + return ret; + } + + /* + * Wait for completion + */ + for (j = 0; j < BCM539X_SPI_RETRIES; j++) { + ret = bcm539x_read_bytes(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, + &rxbuf8, 1); + if (ret) { + return ret; + } + if (!(rxbuf8 & (1 << 7))) { + break; + } + } + + if (j == BCM539X_SPI_RETRIES) { + return -EIO; + } + + return switch_remove_vlan_dir(dev, vid); +} + +/* + * bcm539x_handle_vlan_create_write + */ +static int bcm539x_handle_vlan_create_write(struct switch_device *dev, + char *buf, int inst) +{ + int vid; + + vid = simple_strtoul(buf, NULL, 0); + if (!vid) { + return -EINVAL; + } + + return switch_create_vlan_dir(dev, vid, + bcm539x_switch_handlers_vlan_dir); +} + +/* + * bcm539x_handle_enable_read + */ +static int bcm539x_handle_enable_read(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + u8_t rxbuf; + int ret; + + ret = bcm539x_read_8(bd, PAGE_PORT_TC, REG_CTRL_MODE, &rxbuf); + if (ret) { + return ret; + } + rxbuf = (rxbuf & (1 << 1)) ? 1 : 0; + + return sprintf(buf, "%d\n", rxbuf); +} + +/* + * bcm539x_handle_enable_write + */ +static int bcm539x_handle_enable_write(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + + return bcm539x_set_mode(bd, buf[0] == '1'); +} + +/* + * bcm539x_handle_enable_vlan_read + */ +static int bcm539x_handle_enable_vlan_read(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + u8_t rxbuf; + int ret; + + ret = bcm539x_read_8(bd, PAGE_VLAN, REG_VLAN_CTRL0, &rxbuf); + if (ret) { + return ret; + } + rxbuf = (rxbuf & (1 << 7)) ? 1 : 0; + + return sprintf(buf, "%d\n", rxbuf); +} + +/* + * bcm539x_handle_enable_vlan_write + */ +static int bcm539x_handle_enable_vlan_write(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + int ret; + + /* + * disable 802.1Q VLANs + */ + if (buf[0] != '1') { + ret = bcm539x_write_8(bd, PAGE_VLAN, REG_VLAN_CTRL0, 0); + return ret; + } + + /* + * enable 802.1Q VLANs + * + * Enable 802.1Q | IVL learning + */ + ret = bcm539x_write_8(bd, PAGE_VLAN, REG_VLAN_CTRL0, + (1 << 7) | (3 << 5)); + if (ret) { + return ret; + } + + /* + * RSV multicast fwd | RSV multicast chk + */ + ret = bcm539x_write_8(bd, PAGE_VLAN, REG_VLAN_CTRL1, + (1 << 2) | (1 << 3)); + if (ret) { + return ret; + } +#if 0 + /* + * Drop invalid VID + */ + ret = bcm539x_write_16(bd, PAGE_VLAN, REG_VLAN_CTRL3, 0x00FF); + if (ret) { + return ret; + } +#endif + return 0; +} + +/* + * bcm539x_handle_port_enable_read + */ +static int bcm539x_handle_port_enable_read(struct switch_device *dev, + char *buf, int inst) +{ + return sprintf(buf, "%d\n", 1); +} + +/* + * bcm539x_handle_port_enable_write + */ +static int bcm539x_handle_port_enable_write(struct switch_device *dev, + char *buf, int inst) +{ + /* + * validate port + */ + if (!(dev->port_mask[0] & (1 << inst))) { + return -EIO; + } + + if (buf[0] != '1') { + printk(KERN_WARNING "switch port[%d] disabling is not supported\n", inst); + } + return 0; +} + +/* + * bcm539x_handle_port_state_read + */ +static int bcm539x_handle_port_state_read(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + int ret; + u16_t link; + + /* + * validate port + */ + if (!(dev->port_mask[0] & (1 << inst))) { + return -EIO; + } + + /* + * check PHY link state - CPU port (port 8) is always up + */ + ret = bcm539x_read_16(bd, PAGE_STATUS, REG_LINK_STATUS, &link); + if (ret) { + return ret; + } + link |= (1 << 8); + + return sprintf(buf, "%d\n", (link & (1 << inst)) ? 1 : 0); +} + +/* + * bcm539x_handle_port_media_read + */ +static int bcm539x_handle_port_media_read(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + int ret; + u16_t link, duplex; + u32_t speed; + + /* + * validate port + */ + if (!(dev->port_mask[0] & (1 << inst))) { + return -EIO; + } + + /* + * check PHY link state first - CPU port (port 8) is always up + */ + ret = bcm539x_read_16(bd, PAGE_STATUS, REG_LINK_STATUS, &link); + if (ret) { + return ret; + } + link |= (1 << 8); + + if (!(link & (1 << inst))) { + return sprintf(buf, "UNKNOWN\n"); + } + + /* + * get link speeda dn duplex - CPU port (port 8) is 1000/full + */ + ret = bcm539x_read_32(bd, PAGE_STATUS, 4, &speed); + if (ret) { + return ret; + } + speed |= (2 << 16); + speed = (speed >> (2 * inst)) & 3; + + ret = bcm539x_read_16(bd, PAGE_STATUS, 8, &duplex); + if (ret) { + return ret; + } + duplex |= (1 << 8); + duplex = (duplex >> inst) & 1; + + return sprintf(buf, "%d%cD\n", + (speed == 0) ? 10 : ((speed == 1) ? 100 : 1000), + duplex ? 'F' : 'H'); +} + +/* + * bcm539x_handle_port_meida_write + */ +static int bcm539x_handle_port_meida_write(struct switch_device *dev, + char *buf, int inst) +{ + struct bcm539x_data *bd = + (struct bcm539x_data *)switch_get_drvdata(dev); + int ret; + u16_t ctrl_word, local_cap, local_giga_cap; + + /* + * validate port (not for CPU port) + */ + if (!(dev->port_mask[0] & (1 << inst) & ~(1 << 8))) { + return -EIO; + } + + /* + * Get the maximum capability from status + * SPI reg[0x00] = PHY[0x0] --- MII control + * SPI reg[0x08] = PHY[0x4] --- MII local capability + * SPI reg[0x12] = PHY[0x9] --- GMII control + */ + ret = bcm539x_read_16(bd, REG_MII_PAGE + inst, (MII_ADVERTISE << 1), &local_cap); + if (ret) { + return ret; + } + ret = bcm539x_read_16(bd, REG_MII_PAGE + inst, (MII_CTRL1000 << 1), &local_giga_cap); + if (ret) { + return ret; + } + + /* Configure to the requested speed */ + if (strncmp(buf, "1000FD", 6) == 0) { + /* speed */ + local_cap &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL); + local_cap &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL); + local_giga_cap |= (ADVERTISE_1000HALF | ADVERTISE_1000FULL); + /* duplex */ + } else if (strncmp(buf, "100FD", 5) == 0) { + /* speed */ + local_cap &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL); + local_cap |= (ADVERTISE_100HALF | ADVERTISE_100FULL); + local_giga_cap &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + /* duplex */ + local_cap &= ~(ADVERTISE_100HALF); + } else if (strncmp(buf, "100HD", 5) == 0) { + /* speed */ + local_cap &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL); + local_cap |= (ADVERTISE_100HALF | ADVERTISE_100FULL); + local_giga_cap &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + /* duplex */ + local_cap &= ~(ADVERTISE_100FULL); + } else if (strncmp(buf, "10FD", 4) == 0) { + /* speed */ + local_cap |= (ADVERTISE_10HALF | ADVERTISE_10FULL); + local_cap &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL); + local_giga_cap &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + /* duplex */ + local_cap &= ~(ADVERTISE_10HALF); + } else if (strncmp(buf, "10HD", 4) == 0) { + /* speed */ + local_cap |= (ADVERTISE_10HALF | ADVERTISE_10FULL); + local_cap &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL); + local_giga_cap &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + /* duplex */ + local_cap &= ~(ADVERTISE_10FULL); + } else if (strncmp(buf, "AUTO", 4) == 0) { + /* speed */ + local_cap |= (ADVERTISE_10HALF | ADVERTISE_10FULL); + local_cap |= (ADVERTISE_100HALF | ADVERTISE_100FULL); + local_giga_cap |= (ADVERTISE_1000HALF | ADVERTISE_1000FULL); + } else { + return -EINVAL; + } + + /* Active PHY with the requested speed for auto-negotiation */ + ret = bcm539x_write_16(bd, REG_MII_PAGE + inst, (MII_ADVERTISE << 1), local_cap); + if (ret) { + return ret; + } + ret = bcm539x_write_16(bd, REG_MII_PAGE + inst, (MII_CTRL1000 << 1), local_giga_cap); + if (ret) { + return ret; + } + + ret = bcm539x_read_16(bd, REG_MII_PAGE + inst, (MII_BMCR << 1), &ctrl_word); + if (ret) { + return ret; + } + ctrl_word |= (BMCR_ANENABLE | BMCR_ANRESTART); + ret = bcm539x_write_16(bd, REG_MII_PAGE + inst, (MII_BMCR << 1), ctrl_word); + if (ret) { + return ret; + } + + return 0; +} + +/* + * proc_fs entries for this switch + */ +static const struct switch_handler bcm539x_switch_handlers[] = { + { + .name = "enable", + .read = bcm539x_handle_enable_read, + .write = bcm539x_handle_enable_write, + }, + { + .name = "enable_vlan", + .read = bcm539x_handle_enable_vlan_read, + .write = bcm539x_handle_enable_vlan_write, + }, + { + .name = "reset", + .write = bcm539x_handle_reset, + }, + { + }, +}; + +/* + * Handlers for /vlan + */ +static const struct switch_handler bcm539x_switch_handlers_vlan[] = { + { + .name = "delete", + .write = bcm539x_handle_vlan_delete_write, + }, + { + .name = "create", + .write = bcm539x_handle_vlan_create_write, + }, + { + }, +}; + +/* + * Handlers for /port/ + */ +static const struct switch_handler bcm539x_switch_handlers_port[] = { + { + .name = "enable", + .read = bcm539x_handle_port_enable_read, + .write = bcm539x_handle_port_enable_write, + }, + { + .name = "state", + .read = bcm539x_handle_port_state_read, + }, + { + .name = "media", + .read = bcm539x_handle_port_media_read, + .write = bcm539x_handle_port_meida_write, + }, + { + }, +}; + +/* + * bcm539x_probe + */ +static int __devinit bcm539x_probe(struct spi_device *spi) +{ + struct bcm539x_data *bd; + struct switch_core_platform_data *pdata; + struct switch_device *switch_dev = NULL; + int i, ret; + u8_t txbuf[2]; + + pdata = spi->dev.platform_data; + if (!pdata) { + return -EINVAL; + } + + ret = spi_setup(spi); + if (ret < 0) { + return ret; + } + + /* + * Reset the chip if requested + */ + if (pdata->flags & SWITCH_DEV_FLAG_HW_RESET) { + ret = gpio_request(pdata->pin_reset, "switch-bcm539x-reset"); + if (ret) { + printk(KERN_WARNING "Could not request reset\n"); + return -EINVAL; + } + + gpio_direction_output(pdata->pin_reset, 0); + udelay(10); + gpio_set_value(pdata->pin_reset, 1); + udelay(20); + } + + /* + * Allocate our private data structure + */ + bd = kzalloc(sizeof(struct bcm539x_data), GFP_KERNEL); + if (!bd) { + return -ENOMEM; + } + + dev_set_drvdata(&spi->dev, bd); + bd->pdata = pdata; + bd->spi = spi; + bd->last_page = 0xFF; + + /* + * First perform SW reset if needed + */ + if (pdata->flags & SWITCH_DEV_FLAG_SW_RESET) { + txbuf[0] = (1 << 7) | (1 << 4); + ret = bcm539x_write_bytes(bd, PAGE_PORT_TC, + REG_CTRL_SRST, txbuf, 1); + if (ret) { + goto fail; + } + + udelay(20); + + txbuf[0] = 0; + ret = bcm539x_write_bytes(bd, PAGE_PORT_TC, + REG_CTRL_SRST, txbuf, 1); + if (ret) { + goto fail; + } + } + + /* + * See if we can see the chip + */ + for (i = 0; i < 10; i++) { + ret = bcm539x_read_bytes(bd, PAGE_MMR, REG_DEVICE_ID, + &bd->device_id, 1); + if (!ret) { + break; + } + } + if (ret) { + goto fail; + } + + /* + * We only support 5395, 5397, 5398 + */ + if ((bd->device_id != 0x95) && (bd->device_id != 0x97) && + (bd->device_id != 0x98)) { + ret = -ENODEV; + goto fail; + } + + /* + * Override CPU port config: fixed link @1000 with flow control + */ + ret = bcm539x_read_8(bd, PAGE_PORT_TC, REG_CTRL_MIIPO, txbuf); + bcm539x_write_8(bd, PAGE_PORT_TC, REG_CTRL_MIIPO, 0xbb); // Override IMP port config + printk("Broadcom SW CPU port setting: 0x%x -> 0xbb\n", txbuf[0]); + + /* + * Setup the switch driver structure + */ + switch_dev = switch_alloc(); + if (!switch_dev) { + ret = -ENOMEM; + goto fail; + } + switch_dev->name = pdata->name; + + switch_dev->ports = (bd->device_id == 0x98) ? 9 : 6; + switch_dev->port_mask[0] = (bd->device_id == 0x98) ? 0x1FF : 0x11F; + switch_dev->driver_handlers = bcm539x_switch_handlers; + switch_dev->reg_handlers = NULL; + switch_dev->vlan_handlers = bcm539x_switch_handlers_vlan; + switch_dev->port_handlers = bcm539x_switch_handlers_port; + + bd->switch_dev = switch_dev; + switch_set_drvdata(switch_dev, (void *)bd); + + ret = switch_register(bd->switch_dev); + if (ret < 0) { + goto fail; + } + + printk(KERN_INFO "bcm53%02x switch chip initialized\n", bd->device_id); + + return ret; + +fail: + if (switch_dev) { + switch_release(switch_dev); + } + dev_set_drvdata(&spi->dev, NULL); + kfree(bd); + return ret; +} + +static int __attribute__((unused)) bcm539x_remove(struct spi_device *spi) +{ + struct bcm539x_data *bd; + + bd = dev_get_drvdata(&spi->dev); + + if (bd->pdata->flags & SWITCH_DEV_FLAG_HW_RESET) { + gpio_free(bd->pdata->pin_reset); + } + + if (bd->switch_dev) { + switch_unregister(bd->switch_dev); + switch_release(bd->switch_dev); + } + + dev_set_drvdata(&spi->dev, NULL); + + kfree(bd); + + return 0; +} + +static struct spi_driver bcm539x_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = bcm539x_probe, + .remove = __devexit_p(bcm539x_remove), +}; + +static int __init bcm539x_init(void) +{ + return spi_register_driver(&bcm539x_driver); +} + +module_init(bcm539x_init); + +static void __exit bcm539x_exit(void) +{ + spi_unregister_driver(&bcm539x_driver); +} +module_exit(bcm539x_exit); + +MODULE_AUTHOR("Pat Tjin"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("bcm539x SPI switch chip driver"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-core.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-core.c new file mode 100644 index 0000000000..855aa068d2 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-core.c @@ -0,0 +1,737 @@ +/* + * arch/ubicom32/mach-common/switch-core.c + * Ubicom32 architecture switch and /proc/switch/... implementation. + * + * (C) Copyright 2009, Ubicom, Inc. + * Copyright (C) 2005 Felix Fietkau + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + * + * Basic doc of driver's /proc interface: + * /proc/switch// + * registers: read-only + * counters: read-only + * reset: write causes hardware reset + * enable: "0", "1" + * enable_vlan: "0", "1" + * port// + * enabled: "0", "1" + * link state: read-only + * media: "AUTO", "1000FD", "100FD", "100HD", "10FD", "10HD" + * vlan// + * ports: same syntax as for nvram's vlan*ports (eg. "1 2 3 4 5*") + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "switch-core.h" + +/* + * Pointer to the root of our filesystem + */ +static struct proc_dir_entry *switch_root; + +/* + * Lock used to manage access to the switch list + */ +DECLARE_RWSEM(switch_list_lock); +EXPORT_SYMBOL_GPL(switch_list_lock); + +/* + * List of switches we are managing + */ +LIST_HEAD(switch_list); +EXPORT_SYMBOL_GPL(switch_list); + +/* + * List of handlers we have + */ +LIST_HEAD(switch_handler_list); +EXPORT_SYMBOL_GPL(switch_handler_list); + +/* + * Keep track of all the handlers we added + */ +struct switch_handler_entry { + struct list_head node; + struct proc_dir_entry *parent; + struct switch_device *dev; + const struct switch_handler *handler; + int inst; +}; + +/* + * Keep track of all VLAN dirs we created + */ +struct switch_vlan_entry { + struct list_head node; + struct proc_dir_entry *pde; + int vlan_id; + const struct switch_handler *handlers; +}; + +/* + * switch_parse_vlan_ports + * Parse the vlan properties written to /vlan//ports + */ +void switch_parse_vlan_ports(struct switch_device *switch_dev, + char *buf, u32_t *untag, + u32_t *ports, u32_t *def) +{ + u32_t tag = 0; + *untag = 0; + *ports = 0; + *def = 0; + + + /* + * Skip any leading spaces + */ + while (isspace(*buf)) { + buf++; + } + + /* + * Parse out the string + */ + while (*buf) { + u32_t port = simple_strtoul(buf, &buf, 10); + u32_t mask = (1 << port); + + /* + * Parse out any flags + */ + while (*buf && !isspace(*buf)) { + switch (*buf++) { + case 't': + tag |= mask; + break; + case '*': + *def |= mask; + break; + } + } + *ports |= mask; + + /* + * Skip any spaces + */ + while (isspace(*buf)) { + buf++; + } + } + + *untag = ~tag & *ports; +} + +/* + * switch_proc_read + * Handle reads from the procfs, dispatches the driver specific handler + */ +static ssize_t switch_proc_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode); + char *page; + int len = 0; + + page = kmalloc(SWITCH_MAX_BUFSZ, GFP_KERNEL); + if (!page) { + return -ENOBUFS; + } + + if (pde->data != NULL) { + struct switch_handler_entry *she = + (struct switch_handler_entry *)pde->data; + if (she->handler->read) { + len += she->handler->read(she->dev, page + len, + she->inst); + } + } + len += 1; + + if (*ppos < len) { + len = min_t(int, len - *ppos, count); + if (copy_to_user(buf, (page + *ppos), len)) { + kfree(page); + return -EFAULT; + } + *ppos += len; + } else { + len = 0; + } + + kfree(page); + + return len; +} + +/* + * switch_proc_write + * Handle writes from the procfs, dispatches the driver specific handler + */ +static ssize_t switch_proc_write(struct file *file, const char *buf, + size_t count, loff_t *data) +{ + struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode); + char *page; + int ret = -EINVAL; + + page = kmalloc(count + 1, GFP_KERNEL); + if (page == NULL) + return -ENOBUFS; + + if (copy_from_user(page, buf, count)) { + kfree(page); + return -EINVAL; + } + page[count] = 0; + + if (pde->data != NULL) { + struct switch_handler_entry *she = + (struct switch_handler_entry *)pde->data; + if (she->handler->write) { + ret = she->handler->write(she->dev, page, she->inst); + if (ret >= 0) { + ret = count; + } + } + } + + kfree(page); + return ret; +} + +/* + * File operations for the proc_fs, we must cast here since proc_fs' definitions + * differ from file_operations definitions. + */ +static struct file_operations switch_proc_fops = { + .read = (ssize_t (*) (struct file *, char __user *, + size_t, loff_t *))switch_proc_read, + .write = (ssize_t (*) (struct file *, const char __user *, + size_t, loff_t *))switch_proc_write, +}; + +/* + * switch_add_handler + */ +static int switch_add_handler(struct switch_device *switch_dev, + struct proc_dir_entry *parent, + const struct switch_handler *handler, + int inst) +{ + struct switch_handler_entry *she; + struct proc_dir_entry *pde; + int mode; + + she = (struct switch_handler_entry *) + kzalloc(sizeof(struct switch_handler_entry), GFP_KERNEL); + if (!she) { + return -ENOMEM; + } + + INIT_LIST_HEAD(&she->node); + she->parent = parent; + she->dev = switch_dev; + she->inst = inst; + she->handler = handler; + list_add(&she->node, &switch_dev->handlers); + + mode = 0; + if (handler->read != NULL) { + mode |= S_IRUSR; + } + if (handler->write != NULL) { + mode |= S_IWUSR; + } + + pde = create_proc_entry(handler->name, mode, parent); + if (!pde) { + kfree(she); + printk("Failed to create node '%s' in parent %p\n", + handler->name, parent); + return -ENOMEM; + } + pde->data = (void *)she; + pde->proc_fops = &switch_proc_fops; + + return 0; +} + +/* + * switch_add_handlers + */ +static int switch_add_handlers(struct switch_device *switch_dev, + struct proc_dir_entry *parent, + const struct switch_handler *handlers, + int inst) +{ + while (handlers->name) { + int ret = switch_add_handler(switch_dev, + parent, handlers, inst); + if (ret) { + return ret; + } + handlers++; + } + + return 0; +} + +/* + * switch_remove_vlan_dirs + * Removes all vlan directories + * + * Assumes all vlan directories are empty, should be called after + * switch_remove_handlers + */ +static void switch_remove_vlan_dirs(struct switch_device *switch_dev) +{ + struct list_head *pos; + struct list_head *tmp; + struct switch_vlan_entry *sve; + + list_for_each_safe(pos, tmp, &switch_dev->vlan_dirs) { + sve = list_entry(pos, struct switch_vlan_entry, node); + list_del(pos); + remove_proc_entry(sve->pde->name, switch_dev->vlan_dir); + kfree(sve); + } +} + +/* + * switch_remove_handlers + * Removes all handlers registered to the given switch_device + */ +static void switch_remove_handlers(struct switch_device *switch_dev) +{ + struct list_head *pos; + struct list_head *tmp; + struct switch_handler_entry *she; + + list_for_each_safe(pos, tmp, &switch_dev->handlers) { + she = list_entry(pos, struct switch_handler_entry, node); + list_del(pos); + remove_proc_entry(she->handler->name, she->parent); + kfree(she); + } +} + +/* + * switch_unregister_proc_nodes + * Unregisters all proc nodes related to switch_dev + */ +void switch_unregister_proc_nodes(struct switch_device *switch_dev) +{ + switch_remove_handlers(switch_dev); + + if (switch_dev->port_dirs) { + int i; + + for (i = 0; i < switch_dev->ports; i++) { + if (switch_dev->port_dirs[i]) { + remove_proc_entry( + switch_dev->port_dirs[i]->name, + switch_dev->port_dir); + } + } + } + + if (switch_dev->port_dir) { + remove_proc_entry("port", switch_dev->driver_dir); + switch_dev->port_dir = NULL; + } + + if (switch_dev->reg_dir) { + remove_proc_entry("reg", switch_dev->reg_dir); + switch_dev->reg_dir = NULL; + } + + if (switch_dev->vlan_dir) { + switch_remove_vlan_dirs(switch_dev); + remove_proc_entry("vlan", switch_dev->driver_dir); + switch_dev->vlan_dir = NULL; + } + + if (switch_dev->driver_dir) { + remove_proc_entry(switch_dev->name, switch_root); + switch_dev->driver_dir = NULL; + } +} + +/* + * switch_remove_vlan_dir + * Removes vlan dir in switch//vlan/ + */ +int switch_remove_vlan_dir(struct switch_device *switch_dev, int vlan_id) +{ + struct list_head *pos; + struct switch_vlan_entry *sve = NULL; + + list_for_each(pos, &switch_dev->vlan_dirs) { + struct switch_vlan_entry *tmp = + list_entry(pos, struct switch_vlan_entry, node); + if (tmp->vlan_id == vlan_id) { + sve = tmp; + break; + } + } + + if (!sve) { + return -ENOENT; + } + + /* + * Remove it from the list + */ + list_del(pos); + + /* + * Remove the handlers + */ + while (sve->handlers->name) { + remove_proc_entry(sve->handlers->name, sve->pde); + sve->handlers++; + } + + /* + * Remove the proc entry for the dir + */ + remove_proc_entry(sve->pde->name, switch_dev->vlan_dir); + + kfree(sve); + + return 0; +} + +/* + * switch_create_vlan_dir + * Creates vlan dir in switch//vlan/ + */ +int switch_create_vlan_dir(struct switch_device *switch_dev, + int vlan_id, const struct switch_handler *handlers) +{ + char s[14]; + struct proc_dir_entry *pde = NULL; + struct switch_vlan_entry *sve = NULL; + int ret; + struct list_head *pos; + + /* + * Check to see if it exists already + */ + list_for_each(pos, &switch_dev->vlan_dirs) { + sve = list_entry(pos, struct switch_vlan_entry, node); + if (sve->vlan_id == vlan_id) { + return -EEXIST; + } + } + sve = NULL; + + /* + * Create the vlan directory if we didn't have it before + */ + if (!switch_dev->vlan_dir) { + switch_dev->vlan_dir = proc_mkdir("vlan", + switch_dev->driver_dir); + if (!switch_dev->vlan_dir) { + goto fail; + } + if (switch_dev->vlan_handlers) { + ret = switch_add_handlers(switch_dev, + switch_dev->vlan_dir, + switch_dev->vlan_handlers, 0); + if (ret) { + goto fail; + } + } + } + + /* + * Create the vlan_id directory + */ + snprintf(s, 14, "%d", vlan_id); + pde = proc_mkdir(s, switch_dev->vlan_dir); + if (!pde) { + goto fail; + } + + /* + * Create the handlers for this vlan + */ + if (handlers) { + ret = switch_add_handlers(switch_dev, pde, handlers, vlan_id); + if (ret) { + goto fail; + } + } + + /* + * Keep track of all the switch vlan entries created + */ + sve = (struct switch_vlan_entry *) + kzalloc(sizeof(struct switch_vlan_entry), GFP_KERNEL); + if (!sve) { + goto fail; + } + INIT_LIST_HEAD(&sve->node); + sve->handlers = handlers; + sve->vlan_id = vlan_id; + sve->pde = pde; + list_add(&sve->node, &switch_dev->vlan_dirs); + + return 0; + +fail: + if (sve) { + kfree(sve); + } + + if (pde) { + /* + * Remove any proc entries we might have created + */ + while (handlers->name) { + remove_proc_entry(handlers->name, pde); + handlers++; + } + + remove_proc_entry(s, switch_dev->driver_dir); + } + + return -ENOMEM; +} + +/* + * switch_register_proc_nodes + */ +int switch_register_proc_nodes(struct switch_device *switch_dev) +{ + int i; + int n; + + switch_dev->port_dirs = kzalloc(switch_dev->ports * + sizeof(struct proc_dir_entry *), + GFP_KERNEL); + if (!switch_dev->port_dirs) { + return -ENOMEM; + } + + /* + * Create a new proc entry for this switch + */ + switch_dev->driver_dir = proc_mkdir(switch_dev->name, switch_root); + if (!switch_dev->driver_dir) { + goto fail; + } + if (switch_dev->driver_handlers) { + switch_add_handlers(switch_dev, + switch_dev->driver_dir, + switch_dev->driver_handlers, + 0); + } + + /* + * Create the ports + */ + switch_dev->port_dir = proc_mkdir("port", switch_dev->driver_dir); + if (!switch_dev->port_dir) { + goto fail; + } + for (n = 0, i = 0; i < (SWITCH_PORT_MASK_SIZE * 32); i++) { + if (switch_dev->port_mask[i / 32] & (1 << i % 32)) { + char s[14]; + + snprintf(s, 14, "%d", i); + switch_dev->port_dirs[n] = + proc_mkdir(s, switch_dev->port_dir); + if (!switch_dev->port_dirs[n]) { + goto fail; + } + if (switch_dev->port_handlers) { + switch_add_handlers(switch_dev, + switch_dev->port_dirs[n], + switch_dev->port_handlers, + i); + } + n++; + } + } + + /* + * Create the register directory for switch register access. + */ + if (switch_dev->reg_handlers) { + switch_dev->reg_dir = proc_mkdir("reg", switch_dev->driver_dir); + if (!switch_dev->reg_dir) { + goto fail; + } + + switch_add_handlers(switch_dev, + switch_dev->reg_dir, + switch_dev->reg_handlers, + 0); + } + + /* + * Create the vlan directory + */ + if (switch_dev->vlan_handlers) { + switch_dev->vlan_dir = proc_mkdir("vlan", + switch_dev->driver_dir); + if (!switch_dev->vlan_dir) { + goto fail; + } + if (switch_dev->vlan_handlers) { + switch_add_handlers(switch_dev, + switch_dev->vlan_dir, + switch_dev->vlan_handlers, + 0); + } + } + + return 0; + +fail: + switch_unregister_proc_nodes(switch_dev); + return -ENOMEM; +} + +/* + * switch_release + */ +void switch_release(struct switch_device *switch_dev) +{ + kfree(switch_dev); +} + +/* + * switch_alloc + */ +struct switch_device *switch_alloc(void) +{ + struct switch_device *switch_dev = + kzalloc(sizeof(struct switch_device), + GFP_KERNEL); + INIT_LIST_HEAD(&switch_dev->node); + INIT_LIST_HEAD(&switch_dev->vlan_dirs); + INIT_LIST_HEAD(&switch_dev->handlers); + return switch_dev; +} + +/* + * switch_register + */ +int switch_register(struct switch_device *switch_dev) +{ + int ret; + int i; + + /* + * Make sure that the number of ports and the port mask make sense + */ + for (ret = 0, i = 0; i < (SWITCH_PORT_MASK_SIZE * 32); i++) { + if (switch_dev->port_mask[i / 32] & (1 << i % 32)) { + ret++; + } + } + if (ret > switch_dev->ports) { + return -EINVAL; + } + + /* + * Create the /proc entries + */ + ret = switch_register_proc_nodes(switch_dev); + if (ret) { + return ret; + } + + /* + * Add it to the list of switches + */ + down_write(&switch_list_lock); + list_add_tail(&switch_dev->node, &switch_list); + up_write(&switch_list_lock); + + printk(KERN_INFO "Registered switch device: %s\n", switch_dev->name); + + return 0; +} +EXPORT_SYMBOL_GPL(switch_register); + +/* + * switch_unregister + * Unregisters a previously registered switch_device object + */ +void switch_unregister(struct switch_device *switch_dev) +{ + /* + * remove the proc entries + */ + switch_unregister_proc_nodes(switch_dev); + + /* + * Remove it from the list of switches + */ + down_write(&switch_list_lock); + list_del(&switch_dev->node); + up_write(&switch_list_lock); + + printk(KERN_INFO "Unregistered switch device: %s\n", switch_dev->name); +} +EXPORT_SYMBOL_GPL(switch_unregister); + +/* + * switch_init + */ +static int __init switch_init(void) +{ + switch_root = proc_mkdir("switch", NULL); + if (!switch_root) { + printk(KERN_WARNING "Failed to make root switch node\n"); + return -ENODEV; + } + return 0; +} +module_init(switch_init); + +/* + * switch_exit + */ +static void __exit switch_exit(void) +{ + remove_proc_entry("switch", NULL); +} +module_exit(switch_exit); + +MODULE_AUTHOR("Patrick Tjin"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Ethernet Switch Class Interface"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-core.h b/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-core.h new file mode 100644 index 0000000000..1e18b1cba8 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-core.h @@ -0,0 +1,92 @@ +/* + * arch/ubicom32/mach-common/switch-core.h + * Private data for the switch module + * + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _SWITCH_CORE_H_ +#define _SWITCH_CORE_H_ + +struct switch_handler_entry; +struct switch_vlan_entry; + +#define SWITCH_PORT_MASK_SIZE 2 + +struct switch_device { + struct list_head node; + + const char *name; + void *drvdata; + + u8_t ports; + + struct proc_dir_entry *driver_dir; + const struct switch_handler *driver_handlers; + + struct proc_dir_entry *port_dir; + struct proc_dir_entry **port_dirs; + const struct switch_handler *port_handlers; + + struct proc_dir_entry *reg_dir; + const struct switch_handler *reg_handlers; + + struct proc_dir_entry *vlan_dir; + const struct switch_handler *vlan_handlers; + struct list_head vlan_dirs; + + struct list_head handlers; + + u32_t port_mask[SWITCH_PORT_MASK_SIZE]; +}; + +typedef int (*switch_handler_fn)(struct switch_device *, char *buf, int nr); +struct switch_handler { + const char *name; + + switch_handler_fn read; + switch_handler_fn write; +}; + +#define SWITCH_MAX_BUFSZ 4096 + +static inline void switch_set_drvdata(struct switch_device *switch_dev, void *drvdata) +{ + switch_dev->drvdata = drvdata; +} + +static inline void *switch_get_drvdata(struct switch_device *switch_dev) +{ + return switch_dev->drvdata; +} + +extern int switch_create_vlan_dir(struct switch_device *switch_dev, int vlan_id, const struct switch_handler *handlers); +extern int switch_remove_vlan_dir(struct switch_device *switch_dev, int vlan_id); +extern void switch_parse_vlan_ports(struct switch_device *switch_dev, char *buf, u32_t *untag, u32_t *ports, u32_t *def); + +extern void switch_release(struct switch_device *switch_dev); +extern struct switch_device *switch_alloc(void); +extern int switch_register(struct switch_device *switch_dev); +extern void switch_unregister(struct switch_device *switch_dev); + +#endif diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubi32-gpio.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubi32-gpio.c new file mode 100644 index 0000000000..5fa22f52b4 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_PROC_FS) +#include +#endif + +#include +#include +#include + +#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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32hid.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32hid.c new file mode 100644 index 0000000000..3318eff88b --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input.c new file mode 100644 index 0000000000..e2e0c24d62 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +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 "); +MODULE_DESCRIPTION("Ubicom32 Input Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ubicom32-input"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input_i2c.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input_i2c.c new file mode 100644 index 0000000000..b81f1919ca --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include + +#include + +#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 "); +MODULE_DESCRIPTION("Ubicom32 Input Driver I2C"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ubicom32-input"); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/usb.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/usb.c new file mode 100644 index 0000000000..e13f640544 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/usb.c @@ -0,0 +1,132 @@ +/* + * arch/ubicom32/mach-common/ip5k_usb.c + * Ubicom32 architecture usb support. + * + * (C) Copyright 2009, Ubicom, Inc. + * Copyright (C) 2007 MontaVista Software, Inc. + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/usb_tio.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/usb_tio.c new file mode 100644 index 0000000000..95ace6db71 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#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; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/usb_tio.h b/target/linux/ubicom32/files/arch/ubicom32/mach-common/usb_tio.h new file mode 100644 index 0000000000..a88ea18d59 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/vdc_tio.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/vdc_tio.c new file mode 100644 index 0000000000..cde0cb20f1 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include + +#include +#include + +/* + * 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"); + } +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/Kconfig b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/Kconfig new file mode 100644 index 0000000000..b53d239657 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/Kconfig @@ -0,0 +1,28 @@ + +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 + help + IP5170 Digital Picture Frame board, 8005-1113, IP5K-BEV-0011-13 v1.3 + +config IP5160DEV + bool "IP5160Dev_Ver1Dot1" + select UBICOM32_V3 + help + Ubicom StreamEngine 5000 Development Board, IP5K-BDV-0004-11 v1.1 + +config IP5160EVAL + bool "IP5160RGWEval_Ver2Rev2" + select UBICOM32_V3 + help + Ubicom StreamEngine 5000 RGW Evaluation Board, IP5K-RGW-0004-11 v2.2 diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/Makefile b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/Makefile new file mode 100644 index 0000000000..d02f339bd9 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/Makefile @@ -0,0 +1,31 @@ +# +# 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 . +# +# 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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5160dev.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5160dev.c new file mode 100644 index 0000000000..4fb130d518 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5160dev.c @@ -0,0 +1,109 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +#include +#include +#ifdef CONFIG_SERIAL_UBI32_SERDES +#include +#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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5160rgw.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5160rgw.c new file mode 100644 index 0000000000..fb928f79db --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5160rgw.c @@ -0,0 +1,75 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include + +/* + * 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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5170dpf.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5170dpf.c new file mode 100644 index 0000000000..ef04dcaa87 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip5k/board-ip5170dpf.c @@ -0,0 +1,279 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* + * 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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/Kconfig b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/Kconfig new file mode 100644 index 0000000000..8a7f2f1cf0 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/Kconfig @@ -0,0 +1,205 @@ +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 LCD_CLASS_DEVICE + select LCD_UBICOM32POWER + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + select BACKLIGHT_UBICOM32 + select SND_UBI32 + select MMC_UBICOM32 + select MMC + select MMC_BLOCK + 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 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 IP7160RGWLCD + bool "IP7160RGWLCD" + select UBICOM32_V4 + select UBICOM_INPUT + select NEW_LEDS + select LEDS_CLASS + select LEDS_GPIO + select SPI + select SPI_UBICOM32_GPIO + select VLAN_8021Q + select UBICOM_SWITCH + select UBICOM_SWITCH_BCM539X + select INPUT_TOUCHSCREEN + select TOUCHSCREEN_TSC2007 + select FB + select FB_UBICOM32_VIRTUAL + select I2C + select I2C_GPIO + help + Ubicom IP7160 RGW Eval, supports: + 8007-0112 v1.2 + 8007-1410 v1.0 + + With Ubicom LCD Adapter + 8007-0920 v2.0 + 8007-0921 v2.1 + + +config IP7160BRINGUP + bool "IP7160BRINGUP" + select UBICOM32_V4 + select NEW_LEDS + select LEDS_CLASS + select LEDS_GPIO + 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 + help + IP7160 Digital Picture Frame board, supports: + 8007-0211 Rev 1.1 + +config IP7500MODULE + bool "IP7500MODULE" + select UBICOM32_V4 + 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 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 FB_UBICOM32_VIRTUAL + select FB_UBICOM32_VIRTUAL_NOAUTO + select LCD_CLASS_DEVICE + select LCD_UBICOM32POWER + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + select BACKLIGHT_UBICOM32 + select INPUT_TOUCHSCREEN + select TOUCHSCREEN_TSC2007 + select SOUND + select SND + select SND_UBI32 + select SND_UBI32_AUDIO_CS4350 + select MMC_UBICOM32 + select MMC + select MMC_BLOCK + help + IP7500 Media Board w/ IP7500 CPU Module board, supports: + 8007-0610 v1.0 w/ 8007-0510 v1.0 + 8007-0610 v1.0 w/ 8007-0510 v1.0 NOPHY + 8007-0610 v1.0 w/ 8007-0511 v1.1 NOPHY + + Also supports optional LCD Adapter board: + 8006-0920 v2.0 + 8006-0921 v2.1 + + Please see ip7500media.c for more details. + +config IP7500WSPKR + bool "IP7500WSPKR" + select UBICOM32_V4 + select I2C + select I2C_GPIO + select SOUND + select SND + select SND_UBI32 + select SND_UBI32_AUDIO_CS4350 + help + IP7500 Wireless Speaker Board, supports: + 8007-1210 v1.0 + + Please see ip7500wspkr.c for more details. + +config IP7500IAP + bool "IP7500IAP" + select UBICOM32_V4 + select I2C + select I2C_GPIO + select FB + select FB_UBICOM32_VIRTUAL + select SOUND + select SND + select SND_UBI32 + select SND_UBI32_AUDIO_CS4350 + select RTC_CLASS + select RTC_DRV_S35390A + select INPUT_TOUCHSCREEN + select TOUCHSCREEN_TSC2007 + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + select BACKLIGHT_UBICOM32 + help + IP7500 Internet Audio Player, supports: + 8007-1110 v1.0 + + Please see ip7500iap.c for more details. + + + Please see ip7500media.c for more details. diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/Makefile b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/Makefile new file mode 100644 index 0000000000..f9df397eea --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/Makefile @@ -0,0 +1,38 @@ +# +# 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 . +# +# 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_IP7160RGWLCD) += 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 +obj-$(CONFIG_IP7500WSPKR) += board-ip7500wspkr.o +obj-$(CONFIG_IP7500IAP) += board-ip7500iap.o diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7145dpf.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7145dpf.c new file mode 100644 index 0000000000..d3348d30f7 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7145dpf.c @@ -0,0 +1,715 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/****************************************************************************** + * 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 +#include +#include +#include + +#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 ubicom32lcdpower_platform_data ip7145dpf_lcdpower_data = { + .vgh_gpio = GPIO_RE_6, + .vgh_polarity = true, +}; + +static struct platform_device ip7145dpf_lcdpower_device = { + .name = "ubicom32lcdpower", + .id = -1, + .dev = { + .platform_data = &ip7145dpf_lcdpower_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_lcdpower_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_device_alloc("snd-ubi32-generic", "audio", "audio-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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160bringup.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160bringup.c new file mode 100644 index 0000000000..2527d8d7d3 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160bringup.c @@ -0,0 +1,134 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SERIAL_UBI32_SERDES +#include +#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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160dpf.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160dpf.c new file mode 100644 index 0000000000..7f4e4f3e45 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160dpf.c @@ -0,0 +1,326 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* + * 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, 1); + 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_device_alloc("snd-ubi32-cs4350", "audio-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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160rgw.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160rgw.c new file mode 100644 index 0000000000..4d50375a7d --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7160rgw.c @@ -0,0 +1,355 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SERIAL_UBI32_SERDES +#include +#endif + +#include +#include + +#ifdef CONFIG_IP7160RGWLCD +#include +#include +/* + * LCD Adapter board 8007-092x support + * + * Touch controller + * + * Connected via I2C bus, interrupt on PA6 + */ +#include + +/* + * ip7160rgwlcd_tsc2007_exit_platform_hw + */ +static void ip7160rgwlcd_tsc2007_exit_platform_hw(void) +{ + UBICOM32_IO_PORT(RA)->ctl0 &= ~(0x03 << 17); + gpio_free(GPIO_RA_5); +} + +/* + * ip7160rgwlcd_tsc2007_init_platform_hw + */ +static int ip7160rgwlcd_tsc2007_init_platform_hw(void) +{ + int res = gpio_request(GPIO_RA_5, "TSC2007_IRQ"); + if (res) { + return res; + } + + UBICOM32_IO_PORT(RA)->ctl0 &= ~(0x03 << 17); + UBICOM32_IO_PORT(RA)->ctl0 |= (0x02 << 17); + return 0; +} + +/* + * ip7160rgwlcd_tsc2007_get_pendown_state + */ +static int ip7160rgwlcd_tsc2007_get_pendown_state(void) +{ + return !gpio_get_value(GPIO_RA_5); +} + +static struct tsc2007_platform_data ip7160rgwlcd_tsc2007_data = { + .model = 2007, + .x_plate_ohms = 350, + .get_pendown_state = ip7160rgwlcd_tsc2007_get_pendown_state, + .init_platform_hw = ip7160rgwlcd_tsc2007_init_platform_hw, + .exit_platform_hw = ip7160rgwlcd_tsc2007_exit_platform_hw, +}; + +/****************************************************************************** + * I2C bus on the board, SDA PI14, SCL PI13 + */ +static struct i2c_gpio_platform_data ip7160rgwlcd_i2c_data = { + .sda_pin = GPIO_RI_14, + .scl_pin = GPIO_RI_13, + .sda_is_open_drain = 0, + .scl_is_open_drain = 0, + .udelay = 50, +}; + +static struct platform_device ip7160rgwlcd_i2c_device = { + .name = "i2c-gpio", + .id = 0, + .dev = { + .platform_data = &ip7160rgwlcd_i2c_data, + }, +}; + +static struct i2c_board_info __initdata ip7160rgwlcd_i2c_board_info[] = { + { + .type = "tsc2007", + .addr = 0x48, + .irq = 45, // RA5 + .platform_data = &ip7160rgwlcd_tsc2007_data, + }, +}; + +#endif + +/* + * 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_core_platform_data ip7160rgw_bcm539x_platform_data = { + .flags = SWITCH_DEV_FLAG_HW_RESET, + .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, +#ifdef CONFIG_IP7160RGWLCD + &ip7160rgwlcd_i2c_device, +#endif +}; + +/* + * 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(); + + /* + * Reserve switch SPI CS on behalf on switch driver + */ + if (gpio_request(ip7160rgw_bcm539x_controller_data.pin_cs, "switch-bcm539x-cs")) { + printk(KERN_WARNING "Could not request cs of switch SPI I/F\n"); + return -EIO; + } + gpio_direction_output(ip7160rgw_bcm539x_controller_data.pin_cs, 1); + + 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)); + +#ifdef CONFIG_IP7160RGWLCD + printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); + i2c_register_board_info(0, ip7160rgwlcd_i2c_board_info, ARRAY_SIZE(ip7160rgwlcd_i2c_board_info)); + printk(KERN_INFO "IP7160 RGW + LCD\n"); +#else + printk(KERN_INFO "IP7160 RGW\n"); +#endif + return 0; +} + +arch_initcall(ip7160rgw_init); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500av.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500av.c new file mode 100644 index 0000000000..7cd25fcd39 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500av.c @@ -0,0 +1,273 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************** + * 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_vdac_write + */ +static int __init ip7500av_vdac_write(int reg, int val) +{ + struct i2c_adapter *adap; + struct i2c_msg msg[1]; + unsigned char data[2]; + int err; + + adap = i2c_get_adapter(0); + if (!adap) { + printk(KERN_WARNING "%s: failed to get i2c adapter\n", __FUNCTION__); + return -ENODEV; + } + msg->addr = 0x2B; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + data[0] = reg; + data[1] = val; + err = i2c_transfer(adap, msg, 1); + i2c_put_adapter(adap); + if (err >= 0) { + return 0; + } + return err; +} + +/* + * ip7500av_vdac_init + * Initializes the video DAC via I2C + * + * Equivalent mode line: 720x480p = 27 Mhz, 720 736 800 858 480 484 492 525 + */ +static int __init ip7500av_vdac_init(void) +{ + int err; + + printk(KERN_INFO "Initializing ADV7393 DAC\n"); + + /* + * Reset the VDAC + */ + if (gpio_request(GPIO_RF_6, "VDAC Reset")) { + printk(KERN_WARNING "%s: failed to allocate VDAC Reset\n", __FUNCTION__); + return -EBUSY; + } + gpio_direction_output(GPIO_RF_6, 0); + udelay(1); + gpio_set_value(GPIO_RF_6, 1); + + /* + * See table 100 of ADV7393 data sheet: 16-bit 525p YCrCb In, YPbPr Out + */ + err = ip7500av_vdac_write(0x17, 0x02); + if (err) { + printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); + return err; + } + err = ip7500av_vdac_write(0x00, 0x1c); + if (err) { + printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); + return err; + } + err = ip7500av_vdac_write(0x01, 0x10); + if (err) { + printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); + return err; + } + err = ip7500av_vdac_write(0x31, 0x01); + if (err) { + printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); + return err; + } +#ifdef IP7500AV_VDAC_SWAP_PBPR + err = ip7500av_vdac_write(0x35, 0x08); + if (err) { + printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); + return err; + } +#endif +#ifdef IP7500AV_VDAC_FULL_RANGE + err = ip7500av_vdac_write(0x30, 0x02); + if (err) { + printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); + return err; + } +#endif + return 0; +} +late_initcall(ip7500av_vdac_init); + +/* + * 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(); + + ubi_gpio_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_device_alloc("snd-ubi32-cs4384", "audio", "audio-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_device_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; + + /* + * Reset the DAC + */ + if (gpio_request(GPIO_RF_4, "DAC Reset") == 0) { + gpio_direction_output(GPIO_RF_4, 0); + udelay(1); + gpio_direction_output(GPIO_RF_4, 1); + } else { + printk("Unable to request DAC reset GPIO\n"); + } + } + + /* + * SPDIF port + */ + audio_dev2 = audio_device_alloc("snd-ubi32-generic", "audio", "audio-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)); + + printk(KERN_INFO "IP7500 Audio/Video Board\n"); + return 0; +} +arch_initcall(ip7500av_init); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500iap.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500iap.c new file mode 100644 index 0000000000..f565cdda17 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500iap.c @@ -0,0 +1,414 @@ +/* + * arch/ubicom32/mach-ip7k/board-ip7500iap.c + * Support for IP7500 Internet Audio Player + * + * This file supports the IP7500 Internet Audio Player: + * 8007-1110 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 . + */ +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/****************************************************************************** + * SD/IO Port F (Slot 1) platform data + */ +static struct resource ip7500iap_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 ip7500iap_portf_sd_cards[] = { + [0] = { + .pin_wp = GPIO_RF_7, + .wp_polarity = 1, + .pin_pwr = GPIO_RF_8, + .pin_cd = GPIO_RF_6, + }, +}; + +static struct ubicom32sd_platform_data ip7500iap_portf_sd_platform_data = { + .ncards = 1, + .cards = ip7500iap_portf_sd_cards, +}; + +static struct platform_device ip7500iap_portf_sd_device = { + .name = "ubicom32sd", + .id = 0, + .resource = ip7500iap_portf_sd_resources, + .num_resources = ARRAY_SIZE(ip7500iap_portf_sd_resources), + .dev = { + .platform_data = &ip7500iap_portf_sd_platform_data, + }, + +}; + +/* + * ip7500iap_portf_sd_init + */ +static void ip7500iap_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 + */ + ip7500iap_portf_sd_resources[0].start = sd_node->dn.sendirq; + ip7500iap_portf_sd_resources[1].start = sd_node->dn.recvirq; + ip7500iap_portf_sd_resources[2].start = (u32_t)&(sd_node->regs); + ip7500iap_portf_sd_resources[2].end = (u32_t)&(sd_node->regs) + sizeof(sd_node->regs); + + platform_device_register(&ip7500iap_portf_sd_device); +} + +/****************************************************************************** + * SD/IO Port B (Slot 2) platform data + */ +static struct resource ip7500iap_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 ip7500iap_portb_sd_cards[] = { + [0] = { + .pin_wp = GPIO_RB_13, + .wp_polarity = 1, + .pin_pwr = GPIO_RB_11, + .pin_cd = GPIO_RB_12, + }, +}; + +static struct ubicom32sd_platform_data ip7500iap_portb_sd_platform_data = { + .ncards = 1, + .cards = ip7500iap_portb_sd_cards, +}; + +static struct platform_device ip7500iap_portb_sd_device = { + .name = "ubicom32sd", + .id = 1, + .resource = ip7500iap_portb_sd_resources, + .num_resources = ARRAY_SIZE(ip7500iap_portb_sd_resources), + .dev = { + .platform_data = &ip7500iap_portb_sd_platform_data, + }, + +}; + +/* + * ip7500iap_portb_sd_init + */ +static void ip7500iap_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 + */ + ip7500iap_portb_sd_resources[0].start = sd_node->dn.sendirq; + ip7500iap_portb_sd_resources[1].start = sd_node->dn.recvirq; + ip7500iap_portb_sd_resources[2].start = (u32_t)&(sd_node->regs); + ip7500iap_portb_sd_resources[2].end = (u32_t)&(sd_node->regs) + sizeof(sd_node->regs); + + platform_device_register(&ip7500iap_portb_sd_device); +} + +/****************************************************************************** + * Touch controller + * + * Connected via I2C bus, interrupt on PA6 + */ +#include + +/* + * ip7500iap_tsc2007_exit_platform_hw + */ +static void ip7500iap_tsc2007_exit_platform_hw(void) +{ + UBICOM32_IO_PORT(RA)->ctl0 &= ~(0x03 << 19); + gpio_free(GPIO_RA_6); +} + +/* + * ip7500iap_tsc2007_init_platform_hw + */ +static int ip7500iap_tsc2007_init_platform_hw(void) +{ + int res = gpio_request(GPIO_RA_6, "TSC2007_IRQ"); + if (res) { + return res; + } + + UBICOM32_IO_PORT(RA)->ctl0 &= ~(0x03 << 19); + UBICOM32_IO_PORT(RA)->ctl0 |= (0x02 << 19); + return 0; +} + +/* + * ip7500iap_tsc2007_get_pendown_state + */ +static int ip7500iap_tsc2007_get_pendown_state(void) +{ + return !gpio_get_value(GPIO_RA_6); +} + +static struct tsc2007_platform_data ip7500iap_tsc2007_data = { + .model = 2007, + .x_plate_ohms = 350, + .get_pendown_state = ip7500iap_tsc2007_get_pendown_state, + .init_platform_hw = ip7500iap_tsc2007_init_platform_hw, + .exit_platform_hw = ip7500iap_tsc2007_exit_platform_hw, +}; + +/****************************************************************************** + * i2c devices + * + * DO NOT CHANGE THE ORDER HERE unless you know how this works. There + * are hardcoded indicies which refer to the order of drivers listed here. + */ +static struct i2c_board_info __initdata ip7500iap_i2c_board_info[] = { + /* + * U6, CS4350 DAC, address 0x4B + */ + { + .type = "cs4350", + .addr = 0x4B, + }, + + /* + * U20, S35390A RTC, address 0x30 + */ + { + .type = "s35390a", + .addr = 0x30, + }, + + /* + * U9, TSC2007 Touch screen controller, address 0x49, irq RA6 + */ + { + .type = "tsc2007", + .addr = 0x49, + .irq = 46, + .platform_data = &ip7500iap_tsc2007_data, + }, +}; + +/* + * I2C bus on the board, SDA PE4, SCL PE5 + */ +static struct i2c_gpio_platform_data ip7500iap_i2c_data = { + .sda_pin = GPIO_RF_14, + .scl_pin = GPIO_RF_13, + .sda_is_open_drain = 0, + .scl_is_open_drain = 0, + .udelay = 50, +}; + +static struct platform_device ip7500iap_i2c_device = { + .name = "i2c-gpio", + .id = 0, + .dev = { + .platform_data = &ip7500iap_i2c_data, + }, +}; + +/****************************************************************************** + * Backlight on the board PD0, hardware PWM + */ +static struct ubicom32bl_platform_data ip7500iap_backlight_data = { + .type = UBICOM32BL_TYPE_PWM, + .pwm_channel = 2, + .pwm_prescale = 15, + .pwm_period = 60, + .default_intensity = 0x80, +}; + +static struct platform_device ip7500iap_backlight_device = { + .name = "ubicom32bl", + .id = -1, + .dev = { + .platform_data = &ip7500iap_backlight_data, + }, +}; + +/****************************************************************************** + * Devices on this board + */ +static struct platform_device *ip7500iap_devices[] __initdata = { + &ip7500iap_i2c_device, + &ip7500iap_backlight_device, +}; + +/* + * ip7500iap_power_off + * Called to turn the power off for this board + */ +static void ip7500iap_power_off(void) +{ + gpio_set_value(GPIO_RF_11, 0); +} + +/* + * ip7500iap_init + * Called to add the devices which we have on this board + */ +static int __init ip7500iap_init(void) +{ + struct platform_device *audio_dev; + struct platform_device *audio_dev2; + int ret; + + board_init(); + + ubi_gpio_init(); + + /* + * Hold the POWER_HOLD line + */ + ret = gpio_request(GPIO_RF_11, "POWER_HOLD"); + if (ret) { + printk(KERN_ERR "%s: could not request POWER_HOLD GPIO\n", __FUNCTION__); + } + gpio_direction_output(GPIO_RF_11, 1); + mach_power_off = ip7500iap_power_off; + + /* + * DAC nRESET line + */ + ret = gpio_request(GPIO_RE_7, "DAC_nRESET"); + if (ret) { + printk(KERN_ERR "%s: could not request DAC_nRESET GPIO\n", __FUNCTION__); + } + gpio_direction_output(GPIO_RE_7, 0); + udelay(1); + gpio_set_value(GPIO_RE_7, 1); + + /* + * Bring up any SDIO slots + */ + ip7500iap_portb_sd_init(); + ip7500iap_portf_sd_init(); + + /* + * Bring up audio devices + */ + platform_add_devices(ip7500iap_devices, ARRAY_SIZE(ip7500iap_devices)); + + audio_dev = audio_device_alloc("snd-ubi32-cs4350", "audio", "audio-i2sout", 0); + if (audio_dev) { + ip7500iap_i2c_board_info[0].platform_data = audio_dev; + } + + audio_dev2 = audio_device_alloc("snd-ubi32-generic", "audio", "audio-spdifout", 0); + if (audio_dev2) { + platform_device_register(audio_dev2); + } + + printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); + i2c_register_board_info(0, ip7500iap_i2c_board_info, ARRAY_SIZE(ip7500iap_i2c_board_info)); + + printk(KERN_INFO "IP7500 Internet Audio Player\n"); + + return 0; +} + +arch_initcall(ip7500iap_init); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500media.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500media.c new file mode 100644 index 0000000000..3a51aec2a6 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500media.c @@ -0,0 +1,732 @@ +/* + * 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 NOPHY + * P/N 8007-0511 rev 1.1 NOPHY + * 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 + * + * LCD Adapter Board: (optional) + * P/N 8007-0920 rev 2.0 + * P/N 8007-0921 rev 2.1 + * + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/****************************************************************************** + * 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 ubicom32lcdpower_platform_data ip7500media_lcdpower_data = { + .vgh_gpio = GPIO_RE_7, + .vgh_polarity = true, +}; + +static struct platform_device ip7500media_lcdpower_device = { + .name = "ubicom32lcdpower", + .id = -1, + .dev = { + .platform_data = &ip7500media_lcdpower_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, +}; + + +/****************************************************************************** + * Touch controller present on LCD Adapter board + * + * Connected via I2C bus, interrupt on PD1 + */ +#include + +/* + * ip7500media_tsc2007_exit_platform_hw + */ +static void ip7500media_tsc2007_exit_platform_hw(void) +{ + UBICOM32_IO_PORT(RD)->int_mask &= ~(1 << 11); + UBICOM32_IO_PORT(RD)->ctl2 &= ~(0x03 << 16); + gpio_free(GPIO_RD_1); +} + +/* + * ip7500media_tsc2007_init_platform_hw + */ +static int ip7500media_tsc2007_init_platform_hw(void) +{ + int res = gpio_request(GPIO_RD_1, "TSC2007_IRQ"); + if (res) { + return res; + } + UBICOM32_IO_PORT(RD)->function = 0; + UBICOM32_IO_PORT(RD)->int_mask = (1 << 11); + UBICOM32_IO_PORT(RD)->ctl2 &= ~(0x03 << 16); + UBICOM32_IO_PORT(RD)->ctl2 |= (0x02 << 16); + + return 0; +} + +/* + * ip7500media_tsc2007_clear_penirq + */ +static void ip7500media_tsc2007_clear_penirq(void) +{ + UBICOM32_IO_PORT(RD)->int_clr = (1 << 11); +} + +/* + * ip7500media_tsc2007_get_pendown_state + */ +static int ip7500media_tsc2007_get_pendown_state(void) +{ + return !gpio_get_value(GPIO_RD_1); +} + +static struct tsc2007_platform_data ip7500media_tsc2007_data = { + .model = 2007, + .x_plate_ohms = 350, + .get_pendown_state = ip7500media_tsc2007_get_pendown_state, + .init_platform_hw = ip7500media_tsc2007_init_platform_hw, + .exit_platform_hw = ip7500media_tsc2007_exit_platform_hw, + .clear_penirq = ip7500media_tsc2007_clear_penirq, +}; + +/****************************************************************************** + * 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, + }, +}; + +/* + * Additional I2C devices to add when a LCD adapter board is present + */ +static struct i2c_board_info __initdata ip7500media_lcd_adapter_i2c_board_info[] = { + { + I2C_BOARD_INFO("tsc2007", 0x48), + .irq = PORT_OTHER_INT(RD), + .platform_data = &ip7500media_tsc2007_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, + }, +}; + +/* + * Virtual Frame Buffer device for use with LCD Adapter + */ +static struct platform_device ip7500media_vfb_device = { + .name = "ubicom32vfb", + .id = -1, +}; + +/* + * vdc_override: + * 0: no override (auto-detect) + * 1: force vdc usage + * 2: force lcd adapter usage + */ +static int __initdata vdc_override = 0; + +/* + * ip7500media_set_forcevdc + * Called when forcevdc is present on the kernel boot line + */ +static int __init ip7500media_set_forcevdc(char *str) +{ + if (str[0] == '1') { + vdc_override = 1; + } else { + vdc_override = 2; + } + return 1; +} + +/* + * ip7500media_video_init + * Called late to determine what kind of video we have on this board + */ +static int __init ip7500media_video_init(void) +{ + struct i2c_adapter *adap; + struct i2c_msg msg[1]; + unsigned char *data; + unsigned char checksum; + int err; + int i; + + if (vdc_override == 1) { + printk(KERN_INFO "Force VDCTIO mode\n"); + goto no_adapter; + } + if (vdc_override == 2) { + printk(KERN_INFO "Force LCD Adapter Board mode\n"); + return 0; + } + + /* + * Check to see if there is an EEPROM out there. If we see an + * EEPROM then we will assume a LCD Adapter Board (8007-092x) + * exists. + */ + data = kmalloc(256, GFP_KERNEL); + if (!data) { + printk(KERN_WARNING "%s: Failed to allocate memory\n", __FUNCTION__); + return -ENOMEM; + } + + adap = i2c_get_adapter(0); + if (!adap) { + printk(KERN_WARNING "%s: Failed to get i2c adapter\n", __FUNCTION__); + kfree(data); + return -ENODEV; + } + data[0] = 0; + msg->addr = 0x50; + msg->flags = 0; + msg->len = 1; + msg->buf = data; + err = i2c_transfer(adap, msg, 1); + if (err < 0) { + goto no_adapter; + } + + msg->addr = 0x50; + msg->flags = I2C_M_RD; + msg->len = 256; + msg->buf = data; + err = i2c_transfer(adap, msg, 1); + if (err < 0) { + goto no_adapter; + } + + i2c_put_adapter(adap); + + /* + * Verify the checksum + */ + checksum = 0xff; + for (i = 0; i < 255; i++) { + checksum ^= data[i]; + } + if (checksum != data[255]) { + printk(KERN_WARNING "%s: Checksum mismatch\n", __FUNCTION__); + } + + kfree(data); + + /* + * Bring up VFB + */ + platform_device_register(&ip7500media_vfb_device); + + /* + * Add the i2c devices on the LCD Adapter board. (We have to use i2c_new_device + * since it's late in the boot process.) + */ + printk(KERN_INFO "%s: registering LCD Adapter board i2c resources\n", __FUNCTION__); + for (i = 0; i < ARRAY_SIZE(ip7500media_lcd_adapter_i2c_board_info); i++) { + i2c_new_device(adap, &ip7500media_lcd_adapter_i2c_board_info[i]); + } + + i2c_put_adapter(adap); + + return 0; + + /* + * No LCD Adapter board, bring up VDC + */ +no_adapter: + vdc_tio_init(); + return 0; +} +late_initcall(ip7500media_video_init); +__setup("forcevdc=", ip7500media_set_forcevdc); + +/* + * ip7500media_init + * Called to add the devices which we have on this board + */ +static int __init ip7500media_init(void) +{ + struct platform_device *audio_dev; + int have_ethernet = (devtree_find_node("eth_lan") != 0); + + board_init(); + + ubi_gpio_init(); + +#ifdef CONFIG_UIO_UBICOM32RING + ring_tio_init("decoder_ring"); +#endif + + /* + * Register all of the devices we have on this board + */ + printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); + platform_device_register(&ip7500media_i2c_device); + platform_device_register(&ip7500media_backlight_device); + + /* + * If ethernet doesn't exist then we can init the lcdpower + */ + if (!have_ethernet) { + platform_device_register(&ip7500media_lcdpower_device); + } + + /* + * Allocate the audio drivers. SPDIF not supported on boards with ethernet. + */ + audio_dev = audio_device_alloc("snd-ubi32-cs4350", "audio", "audio-i2sout", 0); + if (audio_dev) { + ip7500media_i2c_board_info[0].platform_data = audio_dev; + } + + if (!have_ethernet) { + struct platform_device *audio_dev2; + + audio_dev2 = audio_device_alloc("snd-ubi32-generic", "audio", "audio-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, 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 + */ + + printk("IP7500 Media Board\n"); + + return 0; +} + +arch_initcall(ip7500media_init); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500module.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500module.c new file mode 100644 index 0000000000..c8c223a9fd --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500module.c @@ -0,0 +1,55 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include + +/* + * 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); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500wspkr.c b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500wspkr.c new file mode 100644 index 0000000000..7f27933faa --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-ip7k/board-ip7500wspkr.c @@ -0,0 +1,101 @@ +/* + * arch/ubicom32/mach-ip7k/board-ip7500wspkr.c + * Support for IP7500 Wireless Speaker board. + * + * This file supports the IP7500 Wireless Speaker board: + * 8007-1210 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 . + */ +#include +#include +#include + +#include +#include +#include +#include + +static struct i2c_board_info __initdata ip7500wspkr_i2c_board_info[] = { + /* + * U6, CS4350 DAC, address 0x4B + */ + { + .type = "cs4350", + .addr = 0x4B, + }, +}; + +/* + * I2C bus on the board, SDA PE4, SCL PE5 + */ +static struct i2c_gpio_platform_data ip7500wspkr_i2c_data = { + .sda_pin = GPIO_RD_5, + .scl_pin = GPIO_RD_6, + .sda_is_open_drain = 0, + .scl_is_open_drain = 0, + .udelay = 50, +}; + +static struct platform_device ip7500wspkr_i2c_device = { + .name = "i2c-gpio", + .id = 0, + .dev = { + .platform_data = &ip7500wspkr_i2c_data, + }, +}; + +static struct platform_device *ip7500wspkr_devices[] __initdata = { + &ip7500wspkr_i2c_device, +}; + +/* + * ip7500wspkr_init + * Called to add the devices which we have on this board + */ +static int __init ip7500wspkr_init(void) +{ + struct platform_device *audio_dev; + struct platform_device *audio_dev2; + + board_init(); + + ubi_gpio_init(); + + platform_add_devices(ip7500wspkr_devices, ARRAY_SIZE(ip7500wspkr_devices)); + + audio_dev = audio_device_alloc("snd-ubi32-cs4350", "audio", "audio-i2sout", 0); + if (audio_dev) { + ip7500wspkr_i2c_board_info[0].platform_data = audio_dev; + } + + audio_dev2 = audio_device_alloc("snd-ubi32-generic", "audio", "audio-spdifout", 0); + if (audio_dev2) { + platform_device_register(audio_dev2); + } + + printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); + i2c_register_board_info(0, ip7500wspkr_i2c_board_info, ARRAY_SIZE(ip7500wspkr_i2c_board_info)); + + printk(KERN_INFO "IP7500 Wireless Speaker Board\n"); + + return 0; +} + +arch_initcall(ip7500wspkr_init); diff --git a/target/linux/ubicom32/files/arch/ubicom32/mm/Makefile b/target/linux/ubicom32/files/arch/ubicom32/mm/Makefile new file mode 100644 index 0000000000..222128d2bd --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mm/Makefile @@ -0,0 +1,32 @@ +# +# arch/ubicom32/mm/Makefile +# +# +# (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 . +# +# 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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/mm/fault.c b/target/linux/ubicom32/files/arch/ubicom32/mm/fault.c new file mode 100644 index 0000000000..ce6b1c760c --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mm/fault.c @@ -0,0 +1,80 @@ +/* + * arch/ubicom32/mm/fault.c + * Ubicom32 architecture page fault implementation. + * + * (C) Copyright 2009, Ubicom, Inc. + * Copyright (C) 1998 D. Jeff Dionne , + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include + +#include +#include + +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; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mm/init.c b/target/linux/ubicom32/files/arch/ubicom32/mm/init.c new file mode 100644 index 0000000000..c486835415 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mm/init.c @@ -0,0 +1,262 @@ +/* + * arch/ubicom32/mm/init.c + * Ubicom32 architecture virtual memory initialization. + * + * (C) Copyright 2009, Ubicom, Inc. + * Copyright (C) 1998 D. Jeff Dionne , + * Kenneth Albanowski , + * 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 + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ram_start, ram_end, len; + extern char _etext, _stext, _sdata, _ebss, __init_begin, __init_end; + + 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 */ + processor_dram(&ram_start, &ram_end); + len = (ram_end - ram_start) + OCMSIZE; +#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 +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mm/kmap.c b/target/linux/ubicom32/files/arch/ubicom32/mm/kmap.c new file mode 100644 index 0000000000..48b489ff1a --- /dev/null +++ b/target/linux/ubicom32/files/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, + * Copyright (C) 2000-2002 David McCullough + * + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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) +{ +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mm/memory.c b/target/linux/ubicom32/files/arch/ubicom32/mm/memory.c new file mode 100644 index 0000000000..17075ff534 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mm/memory.c @@ -0,0 +1,58 @@ +/* + * arch/ubicom32/mm/memory.c + * Ubicom32 architecture kernel_map() implementation. + * + * (C) Copyright 2009, Ubicom, Inc. + * Copyright (C) 1998 Kenneth Albanowski , + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * 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; +} diff --git a/target/linux/ubicom32/files/arch/ubicom32/mm/ocm-alloc.c b/target/linux/ubicom32/files/arch/ubicom32/mm/ocm-alloc.c new file mode 100644 index 0000000000..5a10c2938b --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mm/ocm-alloc.c @@ -0,0 +1,487 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +int ubi32_ocm_skbuf_max = 21, ubi32_ocm_skbuf, ubi32_ddr_skbuf; + +/* + * OCM area for storing code + */ +extern asmlinkage void *__ocm_free_begin; +extern asmlinkage void *__ocm_free_end; +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; + + len = sprintf(&buf[len], "--- OCM SKB usage (max RX buf %d)\n" + "(SKB in OCM) %d - (SKB in DDR) %d\n", + ubi32_ocm_skbuf_max, + ubi32_ocm_skbuf, + ubi32_ddr_skbuf); + + len += sprintf(&buf[len], "--- OCM Data Heap Size\n" + "%p-%p %10i\n", + ((void *)&__ocm_free_begin), + ((void *)&__ocm_free_end), + ((unsigned int)&__ocm_free_end) - + ((unsigned int)&__ocm_free_begin)); + + if (_ocm_proc_read(buf, &len, count - len, "Inst Heap", + &ocm_inst_heap)) + goto not_done; + *eof = 1; + not_done: + return len; +} + +static int ocm_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int n, v; + char in[8]; + + if (count > sizeof(in)) + return -EINVAL; + + if (copy_from_user(in, buffer, count)) + return -EFAULT; + in[count-1] = 0; + + printk(KERN_INFO "OCM skb alloc max = %s\n", in); + + n = 0; + v = 0; + while ((in[n] >= '0') && (in[n] <= '9')) { + v = v * 10 + (int)(in[n] - '0'); + n++; + } + + if (v == 0) + return -EINVAL; + + ubi32_ocm_skbuf_max = v; + ubi32_ocm_skbuf = ubi32_ddr_skbuf = 0; + + return count; +} + +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->read_proc = ocm_proc_read; + ptr->write_proc = ocm_proc_write; + return 0; +} +late_initcall(sram_proc_init); +#endif diff --git a/target/linux/ubicom32/files/arch/ubicom32/oprofile/Makefile b/target/linux/ubicom32/files/arch/ubicom32/oprofile/Makefile new file mode 100644 index 0000000000..969d2f7023 --- /dev/null +++ b/target/linux/ubicom32/files/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 . +# +# 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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/oprofile/ipProf.h b/target/linux/ubicom32/files/arch/ubicom32/oprofile/ipProf.h new file mode 100644 index 0000000000..2a785f08d6 --- /dev/null +++ b/target/linux/ubicom32/files/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 diff --git a/target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c b/target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c new file mode 100644 index 0000000000..aeac3c6667 --- /dev/null +++ b/target/linux/ubicom32/files/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 + * . + * + * 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 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* For identifying userland vs kernel address */ +#include +#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); +} diff --git a/target/linux/ubicom32/files/drivers/char/hw_random/ubicom32-rng.c b/target/linux/ubicom32/files/drivers/char/hw_random/ubicom32-rng.c new file mode 100644 index 0000000000..e429589897 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include + +#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"); diff --git a/target/linux/ubicom32/files/drivers/mmc/host/ubicom32sd.c b/target/linux/ubicom32/files/drivers/mmc/host/ubicom32sd.c new file mode 100644 index 0000000000..107c92a746 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/mmc/host/ubicom32sd.c @@ -0,0 +1,773 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#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"); diff --git a/target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c b/target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c new file mode 100644 index 0000000000..73938c8827 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c @@ -0,0 +1,1017 @@ +/* + * Micron SPI-ER NAND Flash Memory + * + * (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 . +*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define NAND_SPI_ER_BLOCK_FROM_ROW(row) (row >> 6) + +#define NAND_SPI_ER_STATUS_P_FAIL (1 << 3) +#define NAND_SPI_ER_STATUS_E_FAIL (1 << 2) +#define NAND_SPI_ER_STATUS_OIP (1 << 0) + +#define NAND_SPI_ER_LAST_ROW_INVALID 0xFFFFFFFF +#define NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET 0x08 + +struct nand_spi_er_device { + const char *name; + + uint8_t id0; + uint8_t id1; + + unsigned int blocks; + unsigned int pages_per_block; + unsigned int page_size; + unsigned int write_size; + unsigned int erase_size; +}; + +struct nand_spi_er { + char name[24]; + + const struct nand_spi_er_device *device; + + struct mutex lock; + struct spi_device *spi; + + struct mtd_info mtd; + + unsigned int last_row; /* the last row we fetched */ + + /* + * Bad block table (MUST be last in strcuture) + */ + unsigned long nbb; + unsigned long bbt[0]; +}; + +const struct nand_spi_er_device nand_spi_er_devices[] = { + { + name: "MT29F1G01ZDC", + id0: 0x2C, + id1: 0x12, + blocks: 1024, + pages_per_block: 64, + page_size: 2048, + write_size: 512, + erase_size: 64 * 2048, + }, + { + name: "MT29F1G01ZDC", + id0: 0x2C, + id1: 0x13, + blocks: 1024, + pages_per_block: 64, + page_size: 2048, + write_size: 512, + erase_size: 64 * 2048, + }, +}; + +static int read_only = 0; +module_param(read_only, int, 0); +MODULE_PARM_DESC(read_only, "Leave device locked"); + +/* + * nand_spi_er_get_feature + * Get Feature register + */ +static int nand_spi_er_get_feature(struct nand_spi_er *chip, int reg, uint8_t *data) +{ + uint8_t txbuf[2]; + uint8_t rxbuf[1]; + int res; + + txbuf[0] = 0x0F; + txbuf[1] = reg; + res = spi_write_then_read(chip->spi, txbuf, 2, rxbuf, 1); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed get feature res=%d\n", chip->name, res); + return res; + } + *data = rxbuf[0]; + return 0; +} + +/* + * nand_spi_er_busywait + * Wait until the chip is not busy + */ +static int nand_spi_er_busywait(struct nand_spi_er *chip, uint8_t *data) +{ + int i; + + for (i = 0; i < 100; i++) { + int res = nand_spi_er_get_feature(chip, 0xC0, data); + if (res) { + return res; + } + if (!(*data & NAND_SPI_ER_STATUS_OIP)) { + break; + } + } + + return 0; +} + +/* + * nand_spi_er_erase + * Erase a block, parameters must be block aligned + */ +static int nand_spi_er_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct nand_spi_er *chip = mtd->priv; + struct spi_device *spi = chip->spi; + uint8_t txbuf[4]; + int res; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: erase addr:%x len:%x\n", chip->name, instr->addr, instr->len); + + if ((instr->addr + instr->len) > mtd->size) { + return -EINVAL; + } + + if (instr->addr & (chip->device->erase_size - 1)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: erase address is not aligned %x\n", chip->name, instr->addr); + return -EINVAL; + } + + if (instr->len & (chip->device->erase_size - 1)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: erase len is not aligned %x\n", chip->name, instr->len); + return -EINVAL; + } + + mutex_lock(&chip->lock); + chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; + + while (instr->len) { + uint32_t block = instr->addr >> 17; + uint32_t row = block << 6; + uint8_t stat; + DEBUG(MTD_DEBUG_LEVEL3, "%s: block erase row:%x block:%x addr:%x rem:%x\n", chip->name, row, block, instr->addr, instr->len); + + /* + * Write enable + */ + txbuf[0] = 0x06; + res = spi_write(spi, txbuf, 1); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); + mutex_unlock(&chip->lock); + return res; + } + + /* + * Test for bad block + */ + if (test_bit(block, chip->bbt)) { + instr->fail_addr = block << 17; + instr->state = MTD_ERASE_FAILED; + res = -EBADMSG; + goto done; + } + + /* + * Block erase + */ + txbuf[0] = 0xD8; + txbuf[1] = 0x00; + txbuf[2] = row >> 8; + txbuf[3] = row & 0xFF; + res = spi_write(spi, txbuf, 4); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed block erase res=%d\n", chip->name, res); + instr->fail_addr = block << 17; + instr->state = MTD_ERASE_FAILED; + goto done; + } + + /* + * Wait + */ + res = nand_spi_er_busywait(chip, &stat); + if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { + instr->fail_addr = block << 17; + instr->state = MTD_ERASE_FAILED; + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); + if (res) { + goto done; + } + + /* + * Chip is stuck? + */ + res = -EIO; + goto done; + } + + /* + * Check the status register + */ + if (stat & NAND_SPI_ER_STATUS_E_FAIL) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: E_FAIL signalled (%02x)\n", chip->name, stat); + instr->fail_addr = block << 17; + instr->state = MTD_ERASE_FAILED; + goto done; + } + + /* + * Next + */ + block++; + instr->len -= chip->device->erase_size; + instr->addr += chip->device->erase_size; + } + + instr->state = MTD_ERASE_DONE; + + mutex_unlock(&chip->lock); + return 0; + +done: + /* + * Write disable + */ + txbuf[0] = 0x04; + res = spi_write(spi, txbuf, 1); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); + } + + mutex_unlock(&chip->lock); + + mtd_erase_callback(instr); + return 0; +} + +/* + * nand_spi_er_read + * + * return -EUCLEAN: ecc error recovered + * return -EBADMSG: ecc error not recovered +*/ +static int nand_spi_er_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct nand_spi_er *chip = mtd->priv; + struct spi_device *spi = chip->spi; + + uint32_t row; + uint32_t column; + int retval = 0; + + *retlen = 0; + DEBUG(MTD_DEBUG_LEVEL2, "%s: read block from %llx len %d into %p\n", chip->name, from, len, buf); + + /* + * Zero length reads, nothing to do + */ + if (len == 0) { + return 0; + } + + /* + * Reject reads which go over the end of the flash + */ + if ((from + len) > mtd->size) { + return -EINVAL; + } + + /* + * Get the row and column address to start at + */ + row = from >> 11; + column = from & 0x7FF; + DEBUG(MTD_DEBUG_LEVEL3, "%s: row=%x %d column=%x %d last_row=%x %d\n", chip->name, row, row, column, column, chip->last_row, chip->last_row); + + /* + * Read the data from the chip + */ + mutex_lock(&chip->lock); + while (len) { + uint8_t stat; + uint8_t txbuf[4]; + struct spi_message message; + struct spi_transfer x[2]; + int res; + size_t toread; + + /* + * Figure out how much to read + * + * If we are reading from the middle of a page then the most we + * can read is to the end of the page + */ + toread = len; + if (toread > (chip->device->page_size - column)) { + toread = chip->device->page_size - column; + } + + DEBUG(MTD_DEBUG_LEVEL3, "%s: buf=%p toread=%x row=%x column=%x last_row=%x\n", chip->name, buf, toread, row, column, chip->last_row); + + if (chip->last_row != row) { + /* + * Check if the block is bad + */ + if (test_bit(NAND_SPI_ER_BLOCK_FROM_ROW(row), chip->bbt)) { + mutex_unlock(&chip->lock); + return -EBADMSG; + } + + /* + * Load the appropriate page + */ + txbuf[0] = 0x13; + txbuf[1] = 0x00; + txbuf[2] = row >> 8; + txbuf[3] = row & 0xFF; + res = spi_write(spi, txbuf, 4); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed page load res=%d\n", chip->name, res); + mutex_unlock(&chip->lock); + return res; + } + + /* + * Wait + */ + res = nand_spi_er_busywait(chip, &stat); + if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); + if (res) { + mutex_unlock(&chip->lock); + return res; + } + + /* + * Chip is stuck? + */ + mutex_unlock(&chip->lock); + return -EIO; + } + + /* + * Check the ECC bits + */ + stat >>= 4; + if (stat == 1) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: ECC recovered, row=%x\n", chip->name, row); + retval = -EUCLEAN; + } + if (stat == 2) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: failed ECC, row=%x\n", chip->name, row); + chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; + mutex_unlock(&chip->lock); + return -EBADMSG; + } + + } + + chip->last_row = row; + + /* + * Read out the data + */ + spi_message_init(&message); + memset(x, 0, sizeof(x)); + + txbuf[0] = 0x03; + txbuf[1] = column >> 8; + txbuf[2] = column & 0xFF; + txbuf[3] = 0; + x[0].tx_buf = txbuf; + x[0].len = 4; + spi_message_add_tail(&x[0], &message); + + x[1].rx_buf = buf; + x[1].len = toread; + spi_message_add_tail(&x[1], &message); + + res = spi_sync(spi, &message); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed data read res=%d\n", chip->name, res); + mutex_unlock(&chip->lock); + return res; + } + buf += toread; + len -= toread; + *retlen += toread; + + /* + * For the next page, increment the row and always start at column 0 + */ + column = 0; + row++; + } + + mutex_unlock(&chip->lock); + return retval; +} + +/* + * nand_spi_er_write + */ +#define NOT_ALIGNED(x) ((x & (device->write_size - 1)) != 0) +static int nand_spi_er_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct nand_spi_er *chip = mtd->priv; + struct spi_device *spi = chip->spi; + const struct nand_spi_er_device *device = chip->device; + uint32_t row; + uint32_t col; + uint8_t txbuf[4]; + int res; + size_t towrite; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: write block to %llx len %d from %p\n", chip->name, to, len, buf); + + *retlen = 0; + + /* + * nothing to write + */ + if (!len) { + return 0; + } + + /* + * Reject writes which go over the end of the flash + */ + if ((to + len) > mtd->size) { + return -EINVAL; + } + + /* + * Check to see if everything is page aligned + */ + if (NOT_ALIGNED(to) || NOT_ALIGNED(len)) { + printk(KERN_NOTICE "nand_spi_er_write: Attempt to write non page aligned data\n"); + return -EINVAL; + } + + mutex_lock(&chip->lock); + chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; + + /* + * If the first write is a partial write then write at most the number of + * bytes to get us page aligned and then the remainder will be + * page aligned. The last bit may be a partial page as well. + */ + col = to & (device->page_size - 1); + towrite = device->page_size - col; + if (towrite > len) { + towrite = len; + } + + /* + * Write the data + */ + row = to >> 11; + while (len) { + struct spi_message message; + struct spi_transfer x[2]; + uint8_t stat; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: write %p to row:%x col:%x len:%x rem:%x\n", chip->name, buf, row, col, towrite, len); + + /* + * Write enable + */ + txbuf[0] = 0x06; + res = spi_write(spi, txbuf, 1); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); + mutex_unlock(&chip->lock); + return res; + } + + /* + * Write the data into the cache + */ + spi_message_init(&message); + memset(x, 0, sizeof(x)); + txbuf[0] = 0x02; + txbuf[1] = col >> 8; + txbuf[2] = col & 0xFF; + x[0].tx_buf = txbuf; + x[0].len = 3; + spi_message_add_tail(&x[0], &message); + x[1].tx_buf = buf; + x[1].len = towrite; + spi_message_add_tail(&x[1], &message); + res = spi_sync(spi, &message); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed cache write res=%d\n", chip->name, res); + goto done; + } + + /* + * Program execute + */ + txbuf[0] = 0x10; + txbuf[1] = 0x00; + txbuf[2] = row >> 8; + txbuf[3] = row & 0xFF; + res = spi_write(spi, txbuf, 4); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed prog execute res=%d\n", chip->name, res); + goto done; + } + + /* + * Wait + */ + res = nand_spi_er_busywait(chip, &stat); + if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); + if (res) { + goto done; + } + + /* + * Chip is stuck? + */ + res = -EIO; + goto done; + } + + if (stat & (1 << 3)) { + res = -EBADMSG; + goto done; + } + + row++; + buf += towrite; + len -= towrite; + *retlen += towrite; + + /* + * At this point, we are always page aligned so start at column 0. + * Note we may not have a full page to write at the end, hence the + * check if towrite > len. + */ + col = 0; + towrite = device->page_size; + if (towrite > len) { + towrite = len; + } + } + + mutex_unlock(&chip->lock); + return res; + +done: + /* + * Write disable + */ + txbuf[0] = 0x04; + res = spi_write(spi, txbuf, 1); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); + } + + mutex_unlock(&chip->lock); + + return res; +} + +/* + * nand_spi_er_isbad + */ +static int nand_spi_er_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_spi_er *chip = mtd->priv; + uint32_t block; + + if (ofs & (chip->device->erase_size - 1)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); + return -EINVAL; + } + + block = ofs >> 17; + + return test_bit(block, chip->bbt); +} + +/* + * nand_spi_er_markbad + */ +static int nand_spi_er_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_spi_er *chip = mtd->priv; + struct spi_device *spi = chip->spi; + uint32_t block; + uint32_t row; + uint8_t txbuf[7]; + int res; + uint8_t stat; + + if (ofs & (chip->device->erase_size - 1)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); + return -EINVAL; + } + + block = ofs >> 17; + + /* + * If it's already marked bad, no need to mark it + */ + if (test_bit(block, chip->bbt)) { + return 0; + } + + /* + * Mark it in our cache + */ + __set_bit(block, chip->bbt); + + /* + * Write the user bad block mark. If it fails, then we really + * can't do anything about it. + */ + mutex_lock(&chip->lock); + chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; + + /* + * Write enable + */ + txbuf[0] = 0x06; + res = spi_write(spi, txbuf, 1); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); + mutex_unlock(&chip->lock); + return res; + } + + /* + * Write the mark + */ + txbuf[0] = 0x84; + txbuf[1] = 0x08; + txbuf[2] = NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET; + txbuf[3] = 0xde; + txbuf[4] = 0xad; + txbuf[5] = 0xbe; + txbuf[6] = 0xef; + res = spi_write(spi, txbuf, 7); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write mark res=%d\n", chip->name, res); + goto done; + } + + /* + * Program execute + */ + row = ofs >> 11; + txbuf[0] = 0x10; + txbuf[1] = 0x00; + txbuf[2] = row >> 8; + txbuf[3] = row & 0xFF; + res = spi_write(spi, txbuf, 4); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed program execute res=%d\n", chip->name, res); + goto done; + } + + /* + * Wait + */ + res = nand_spi_er_busywait(chip, &stat); + if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); + if (res) { + goto done; + } + + /* + * Chip is stuck? + */ + res = -EIO; + goto done; + } + + if (stat & (1 << 3)) { + res = -EBADMSG; + } + +done: + /* + * Write disable + */ + txbuf[0] = 0x04; + res = spi_write(spi, txbuf, 1); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); + } + + mutex_unlock(&chip->lock); + + return res; +} + +/* + * nand_spi_er_read_bbt + */ +static int nand_spi_er_read_bbt(struct nand_spi_er *chip) +{ + int j; + for (j = 0; j < chip->device->blocks; j++) { + uint8_t txbuf[4]; + uint8_t rxbuf[16]; + uint32_t bbmark; + int res; + unsigned short row = j << 6; + uint8_t stat; + + /* + * Read Page + */ + txbuf[0] = 0x13; + txbuf[1] = 0x00; + txbuf[2] = row >> 8; + txbuf[3] = row & 0xFF; + res = spi_write(chip->spi, txbuf, 4); + if (res) { + return res; + } + + /* + * Wait + */ + res = nand_spi_er_busywait(chip, &stat); + if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); + if (res) { + return res; + } + + /* + * Chip is stuck? + */ + return -EIO; + } + + /* + * Check factory bad block mark + */ + txbuf[0] = 0x03; + txbuf[1] = 0x08; + txbuf[2] = 0x00; + txbuf[3] = 0x00; + res = spi_write_then_read(chip->spi, txbuf, 4, rxbuf, 16); + if (res) { + return res; + } + if (rxbuf[0] != 0xFF) { + chip->nbb++; + __set_bit(j, chip->bbt); + continue; + } + + memcpy(&bbmark, &rxbuf[8], 4); + if (bbmark == 0xdeadbeef) { + chip->nbb++; + __set_bit(j, chip->bbt); + } + } + +#if defined(CONFIG_MTD_DEBUG) && (MTD_DEBUG_LEVEL3 <= CONFIG_MTD_DEBUG_VERBOSE) + printk("%s: Bad Block Table:", chip->name); + for (j = 0; j < chip->device->blocks; j++) { + if ((j % 64) == 0) { + printk("\n%s: block %03x: ", chip->name, j); + } + printk("%c", test_bit(j, chip->bbt) ? 'X' : '.'); + } + printk("\n%s: Bad Block Numbers: ", chip->name); + for (j = 0; j < chip->device->blocks; j++) { + if (test_bit(j, chip->bbt)) { + printk("%x ", j); + } + } + printk("\n"); +#endif + + return 0; +} + +#ifndef MODULE +/* + * Called at boot time: + * + * nand_spi_er=read_only + * if read_only specified then do not unlock device + */ +static int __init nand_spi_er_setup(char *str) +{ + if (str && (strncasecmp(str, "read_only", 9) == 0)) { + read_only = 1; + } + return 0; +} + +__setup("nand_spi_er=", nand_spi_er_setup); +#endif + +/* + * nand_spi_er_probe + * Detect and initialize nand_spi_er device. + */ +static int __devinit nand_spi_er_probe(struct spi_device *spi) +{ + uint8_t txbuf[3]; + uint8_t rxbuf[2]; + int i; + int res; + size_t bbt_bytes; + struct nand_spi_er *chip; + const struct nand_spi_er_device *device; + + res = spi_setup(spi); + if (res) { + return res; + } + + /* + * Reset + */ + for (i = 0; i < 2; i++) { + txbuf[0] = 0xFF; + res = spi_write(spi, txbuf, 1); + if (res) { + return res; + } + udelay(250); + } + udelay(1000); + + /* + * Read ID + */ + txbuf[0] = 0x9F; + txbuf[1] = 0x00; + res = spi_write_then_read(spi, txbuf, 2, rxbuf, 2); + if (res) { + return res; + } + + device = nand_spi_er_devices; + for (i = 0; i < ARRAY_SIZE(nand_spi_er_devices); i++) { + if ((device->id0 == rxbuf[0]) && (device->id1 == rxbuf[1])) { + break; + } + device++; + } + if (i == ARRAY_SIZE(nand_spi_er_devices)) { + return -ENODEV; + } + + /* + * Initialize our chip structure + */ + bbt_bytes = DIV_ROUND_UP(device->blocks, BITS_PER_BYTE); + chip = kzalloc(sizeof(struct nand_spi_er) + bbt_bytes, GFP_KERNEL); + if (!chip) { + return -ENOMEM; + } + snprintf(chip->name, sizeof(chip->name), "%s.%d.%d", device->name, spi->master->bus_num, spi->chip_select); + + chip->spi = spi; + chip->device = device; + chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; + + mutex_init(&chip->lock); + + chip->mtd.type = MTD_NANDFLASH; + chip->mtd.flags = MTD_WRITEABLE; + + /* + * #blocks * block size * n blocks + */ + chip->mtd.size = device->blocks * device->pages_per_block * device->page_size; + chip->mtd.erasesize = device->erase_size; + + /* + * 1 page, optionally we can support partial write (512) + */ + chip->mtd.writesize = device->write_size; + chip->mtd.name = device->name; + chip->mtd.erase = nand_spi_er_erase; + chip->mtd.read = nand_spi_er_read; + chip->mtd.write = nand_spi_er_write; + chip->mtd.block_isbad = nand_spi_er_isbad; + chip->mtd.block_markbad = nand_spi_er_markbad; + chip->mtd.priv = chip; + + /* + * Cache the bad block table + */ + res = nand_spi_er_read_bbt(chip); + if (res) { + kfree(chip); + return res; + } + + /* + * Un/lock the chip + */ + txbuf[0] = 0x1F; + txbuf[1] = 0xA0; + if (read_only) { + txbuf[2] = 0x38; + } else { + txbuf[2] = 0x00; + } + res = spi_write(spi, txbuf, 3); + if (res) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: failed lock operation res=%d\n", chip->name, res); + mutex_unlock(&chip->lock); + return res; + } + + spi_set_drvdata(spi, chip); + + printk(KERN_INFO "%s: added device %s size: %u KBytes %u bad blocks %s\n", spi->dev.bus_id, chip->mtd.name, DIV_ROUND_UP(chip->mtd.size, 1024), chip->nbb, read_only ? "[read only]" : ""); + return add_mtd_device(&chip->mtd); +} + +/* + * nand_spi_er_remove + */ +static int __devexit nand_spi_er_remove(struct spi_device *spi) +{ + struct nand_spi_er *chip = spi_get_drvdata(spi); + int status = 0; + + DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", spi->dev.bus_id); + status = del_mtd_device(&chip->mtd); + if (status == 0) + kfree(chip); + return status; +} + +static struct spi_driver nand_spi_er_driver = { + .driver = { + .name = "nand-spi-er", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = nand_spi_er_probe, + .remove = __devexit_p(nand_spi_er_remove), + + /* FIXME: investigate suspend and resume... */ +}; + +/* + * nand_spi_er_init + */ +static int __init nand_spi_er_init(void) +{ + return spi_register_driver(&nand_spi_er_driver); +} +module_init(nand_spi_er_init); + +/* + * nand_spi_er_exit + */ +static void __exit nand_spi_er_exit(void) +{ + spi_unregister_driver(&nand_spi_er_driver); +} +module_exit(nand_spi_er_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick Tjin"); +MODULE_DESCRIPTION("MTD nand_spi_er driver"); diff --git a/target/linux/ubicom32/files/drivers/mtd/devices/ubi32-m25p80.c b/target/linux/ubicom32/files/drivers/mtd/devices/ubi32-m25p80.c new file mode 100644 index 0000000000..405491cc40 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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 %lld\n", + dev_name(&flash->plt_dev->dev), __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", + dev_name(&flash->plt_dev->dev), __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", + dev_name(&flash->plt_dev->dev), __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", + dev_name(&spi->dev), 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 = dev_name(&spi->dev); + + 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 (%lld Kbytes)\n", info->name, + flash->mtd.size / 1024); + + DEBUG(MTD_DEBUG_LEVEL2, + "mtd .name = %s, .size = 0x%.8llx (%lluMiB) " + ".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%.8llx, " + ".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%.8llx, " + ".size = 0x%.8llx (%lluKiB) }\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"); diff --git a/target/linux/ubicom32/files/drivers/mtd/devices/ubi32-nand-spi-er.c b/target/linux/ubicom32/files/drivers/mtd/devices/ubi32-nand-spi-er.c new file mode 100644 index 0000000000..897bed787c --- /dev/null +++ b/target/linux/ubicom32/files/drivers/mtd/devices/ubi32-nand-spi-er.c @@ -0,0 +1,1188 @@ +/* + * Micron SPI-ER NAND Flash Memory + * This code uses the built in Ubicom flash controller + * + * (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 . +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "ubi32-nand-spi-er" +#define UBI32_NAND_SPI_ER_BLOCK_FROM_ROW(row) (row >> 6) + +#define UBI32_NAND_SPI_ER_STATUS_P_FAIL (1 << 3) +#define UBI32_NAND_SPI_ER_STATUS_E_FAIL (1 << 2) +#define UBI32_NAND_SPI_ER_STATUS_OIP (1 << 0) + +#define UBI32_NAND_SPI_ER_LAST_ROW_INVALID 0xFFFFFFFF +#define UBI32_NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET 0x08 + +struct ubi32_nand_spi_er_device { + const char *name; + + uint16_t id; + + unsigned int blocks; + unsigned int pages_per_block; + unsigned int page_size; + unsigned int write_size; + unsigned int erase_size; +}; + +struct ubi32_nand_spi_er { + char name[24]; + + const struct ubi32_nand_spi_er_device *device; + + struct mutex lock; + struct platform_device *pdev; + + struct mtd_info mtd; + + unsigned int last_row; /* the last row we fetched */ + + /* + * Bad block table (MUST be last in strcuture) + */ + unsigned long nbb; + unsigned long bbt[0]; +}; + +/* + * Chip supports a write_size of 512, but we cannot do partial + * page with command 0x84. + * + * We need to use command 0x84 because we cannot fill the FIFO fast + * enough to transfer the whole 512 bytes at a time. (maybe through + * OCM?) + */ +const struct ubi32_nand_spi_er_device ubi32_nand_spi_er_devices[] = { + { + name: "MT29F1G01ZDC", + id: 0x2C12, + blocks: 1024, + pages_per_block: 64, + page_size: 2048, + write_size: 2048, + erase_size: 64 * 2048, + }, + { + name: "MT29F1G01ZDC", + id: 0x2C13, + blocks: 1024, + pages_per_block: 64, + page_size: 2048, + write_size: 2048, + erase_size: 64 * 2048, + }, +}; + +static int read_only = 0; +module_param(read_only, int, 0); +MODULE_PARM_DESC(read_only, "Leave device locked"); + +/* + * Ubicom32 FLASH Command Set + */ +#define FLASH_PORT RA + +#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 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) \ + : "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) \ + : "cc" \ + ); + +#define FLASH_COMMAND_EXEC(io) \ + FLASH_COMMAND_KICK_OFF(io) \ + FLASH_COMMAND_WAIT_FOR_COMPLETION(io) + +/* + * ubi32_nand_spi_er_get_feature + * Get Feature register + */ +static uint8_t ubi32_nand_spi_er_get_feature(uint32_t reg) +{ + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + + /* + * Note that this will produce the sequence: + * SI [0F][REG][00][00] + * SO ---------[SR][SR][SR] + * Since the flash controller can only output 24 bits of address, this is + * ok for this command since the data will just repeat as long as the CS + * is asserted and the clock is running. + */ + io->ctl1 &= ~IO_XFL_CTL1_MASK; + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_RD) | IO_XFL_CTL1_FC_DATA(1) | + IO_XFL_CTL1_FC_ADDR; + io->ctl2 = IO_XFL_CTL2_FC_CMD(0x0F) | IO_XFL_CTL2_FC_ADDR(reg << 16); + FLASH_COMMAND_EXEC(io); + + return io->status1 & 0xFF; +} + +/* + * ubi32_nand_spi_er_write_buf + * writes a buffer to the bus + * + * Writes 511 + 1 bytes to the bus, we have to stuff one data byte into the address. + */ +static void ubi32_nand_spi_er_write_buf(const uint8_t *buf, uint32_t col) +{ + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + uint32_t tmp; + + asm volatile ( + " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_PORTX_INT_FIFO_TX_RESET)") \n\t" + " pipe_flush 0 \n\t" + : + : [port] "a" (FLASH_PORT) + : "cc" + ); + + /* + * Write the data into the cache + */ + io->ctl1 &= ~IO_XFL_CTL1_MASK; +#ifdef SUPPORT_512_FIFO + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | IO_XFL_CTL1_FC_DATA(511) | +#endif + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | IO_XFL_CTL1_FC_DATA(31) | + IO_XFL_CTL1_FC_ADDR; + + /* + * Construct the address with the first byte of data + */ + tmp = (col << 8) | *buf++; + io->ctl2 = IO_XFL_CTL2_FC_CMD(0x84) | IO_XFL_CTL2_FC_ADDR(tmp); + + asm volatile ( + + /* + * Move 32 bytes + * + * The first word needs to be [11][22][33][33] to work around a flash + * controller bug. + */ + " move.2 %[tmp], (%[data])2++ \n\t" + " shmrg.1 %[tmp], (%[data]), %[tmp] \n\t" + " shmrg.1 %[tmp], (%[data])1++, %[tmp] \n\t" + " move.4 "D(IO_TX_FIFO)"(%[port]), %[tmp] \n\t" + + /* + * We're aligned again! + */ + " .rept 7 \n\t" + " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" + " .endr \n\t" + + /* + * Kick off the flash command + */ + " bset "D(IO_INT_CLR)"(%[port]), #0, #%%bit("D(IO_XFL_INT_DONE)") \n\t" + " jmpt.t .+4 \n\t" + " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_XFL_INT_START)") \n\t" + +#ifdef SUPPORT_512_FIFO + /* + * Fill the remaining 120 words as space becomes available + */ + "1: \n\t" + " cmpi "D(IO_FIFO_LEVEL)"(%[port]), #4 \n\t" + " jmpgt.s.t 1b \n\t" + " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" + " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" + " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" + " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" + " add.4 %[cnt], #-4, %[cnt] \n\t" + " jmpgt.t 1b \n\t" +#endif + /* + * Wait for the transaction to finish + */ + " btst "D(IO_INT_STATUS)"(%[port]), #%%bit("D(IO_XFL_INT_DONE)") \n\t" + " jmpeq.f .-4 \n\t" + + : [tmp] "=&d" (tmp), + [data] "+&a" (buf) + : [column] "d" (col), + [port] "a" (FLASH_PORT), + [cnt] "d" (120) // see above comment + : "cc" + ); +} + +/* + * ubi32_nand_spi_er_send_rd_addr + * perform FC_RD: CMD + address + */ +static void ubi32_nand_spi_er_send_rd_addr(uint8_t command, uint32_t address) +{ + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + + 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_ADDR; + io->ctl2 = IO_XFL_CTL2_FC_CMD(command) | IO_XFL_CTL2_FC_ADDR(address); + FLASH_COMMAND_EXEC(io); +} + +/* + * ubi32_nand_spi_er_send_cmd_addr + * perform FC_(xxx): CMD + address + */ +static void ubi32_nand_spi_er_send_cmd_addr(uint8_t command, uint32_t address) +{ + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + + 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(command) | IO_XFL_CTL2_FC_ADDR(address); + FLASH_COMMAND_EXEC(io); +} + +/* + * ubi32_nand_spi_er_write_disable + * clear the write enable bit + */ +static void ubi32_nand_spi_er_write_disable(void) +{ + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + + io->ctl1 &= ~IO_XFL_CTL1_MASK; + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD); + io->ctl2 = IO_XFL_CTL2_FC_CMD(0x04); + FLASH_COMMAND_EXEC(io); +} + +/* + * ubi32_nand_spi_er_write_enable + * set the write enable bit + */ +static void ubi32_nand_spi_er_write_enable(void) +{ + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + + io->ctl1 &= ~IO_XFL_CTL1_MASK; + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD); + io->ctl2 = IO_XFL_CTL2_FC_CMD(0x06); + FLASH_COMMAND_EXEC(io); +} + +/* + * ubi32_nand_spi_er_busywait + * Wait until the chip is not busy + */ +static uint8_t ubi32_nand_spi_er_busywait(void) +{ + int i; + uint8_t data; + + /* + * tRD is 100us, so don't delay too long, however, tERS is + * 10ms so you'd better loop enough. + */ + for (i = 0; i < 200; i++) { + data = ubi32_nand_spi_er_get_feature(0xC0); + if (!(data & UBI32_NAND_SPI_ER_STATUS_OIP)) { + break; + } + + udelay(50); + } + + return data; +} + +/* + * ubi32_nand_spi_er_erase + * Erase a block, parameters must be block aligned + */ +static int ubi32_nand_spi_er_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct ubi32_nand_spi_er *chip = mtd->priv; + int res; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: erase addr:%x len:%x\n", chip->name, instr->addr, instr->len); + + if ((instr->addr + instr->len) > mtd->size) { + return -EINVAL; + } + + if (instr->addr & (chip->device->erase_size - 1)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: erase address is not aligned %x\n", chip->name, instr->addr); + return -EINVAL; + } + + if (instr->len & (chip->device->erase_size - 1)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: erase len is not aligned %x\n", chip->name, instr->len); + return -EINVAL; + } + + mutex_lock(&chip->lock); + chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; + + while (instr->len) { + uint32_t block = instr->addr >> 17; + uint32_t row = block << 6; + uint8_t stat; + DEBUG(MTD_DEBUG_LEVEL3, "%s: block erase row:%x block:%x addr:%x rem:%x\n", chip->name, row, block, instr->addr, instr->len); + + /* + * Test for bad block + */ + if (test_bit(block, chip->bbt)) { + instr->fail_addr = block << 17; + instr->state = MTD_ERASE_FAILED; + res = -EBADMSG; + goto done; + } + + ubi32_nand_spi_er_write_enable(); + + /* + * Block erase + */ + ubi32_nand_spi_er_send_cmd_addr(0xD8, row); + + /* + * Wait + */ + stat = ubi32_nand_spi_er_busywait(); + if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { + instr->fail_addr = block << 17; + instr->state = MTD_ERASE_FAILED; + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); + + /* + * Chip is stuck? + */ + res = -EIO; + goto done; + } + + /* + * Check the status register + */ + if (stat & UBI32_NAND_SPI_ER_STATUS_E_FAIL) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: E_FAIL signalled (%02x)\n", chip->name, stat); + instr->fail_addr = block << 17; + instr->state = MTD_ERASE_FAILED; + goto done; + } + + /* + * Next + */ + block++; + instr->len -= chip->device->erase_size; + instr->addr += chip->device->erase_size; + } + + instr->state = MTD_ERASE_DONE; + + mutex_unlock(&chip->lock); + return 0; + +done: + ubi32_nand_spi_er_write_disable(); + + mutex_unlock(&chip->lock); + + mtd_erase_callback(instr); + return 0; +} + +/* + * ubi32_nand_spi_er_read + * + * return -EUCLEAN: ecc error recovered + * return -EBADMSG: ecc error not recovered +*/ +static int ubi32_nand_spi_er_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct ubi32_nand_spi_er *chip = mtd->priv; + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + + uint32_t row; + uint32_t column; + int retval = 0; + uint32_t *pbuf = (uint32_t *)buf; + + *retlen = 0; + DEBUG(MTD_DEBUG_LEVEL2, "%s: read block from %llx len %d into %p\n", chip->name, from, len, buf); + + /* + * buf should be aligned + */ + if ((uint32_t)buf & 0x03) { + return -EINVAL; + } + + /* + * Zero length reads, nothing to do + */ + if (len == 0) { + return 0; + } + + /* + * Reject reads which go over the end of the flash + */ + if ((from + len) > mtd->size) { + return -EINVAL; + } + + /* + * Get the row and column address to start at + */ + row = from >> 11; + column = from & 0x7FF; + DEBUG(MTD_DEBUG_LEVEL3, "%s: row=%x %d column=%x %d last_row=%x %d\n", chip->name, row, row, column, column, chip->last_row, chip->last_row); + + /* + * Read the data from the chip + */ + mutex_lock(&chip->lock); + while (len) { + uint8_t stat; + size_t toread; + int i; + int tmp; + + /* + * Figure out how much to read + * + * If we are reading from the middle of a page then the most we + * can read is to the end of the page + */ + toread = len; + if (toread > (chip->device->page_size - column)) { + toread = chip->device->page_size - column; + } + + DEBUG(MTD_DEBUG_LEVEL3, "%s: buf=%p toread=%x row=%x column=%x last_row=%x\n", chip->name, pbuf, toread, row, column, chip->last_row); + + if (chip->last_row != row) { + /* + * Check if the block is bad + */ + if (test_bit(UBI32_NAND_SPI_ER_BLOCK_FROM_ROW(row), chip->bbt)) { + mutex_unlock(&chip->lock); + return -EBADMSG; + } + + /* + * Load the appropriate page + */ + ubi32_nand_spi_er_send_cmd_addr(0x13, row); + + /* + * Wait + */ + stat = ubi32_nand_spi_er_busywait(); + if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); + + /* + * Chip is stuck? + */ + mutex_unlock(&chip->lock); + return -EIO; + } + + /* + * Check the ECC bits + */ + stat >>= 4; + if (stat == 1) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: ECC recovered, row=%x\n", chip->name, row); + retval = -EUCLEAN; + } + if (stat == 2) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: failed ECC, row=%x\n", chip->name, row); + chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; + mutex_unlock(&chip->lock); + return -EBADMSG; + } + + } + + chip->last_row = row; + + /* + * Read out the data: + * We can always read a little too much since there is the + * OOB after byte addr 2047. The most we'll overread is 3 bytes. + */ + if (((uint32_t)pbuf & 0x03) == 0) { + /* + * Aligned read + */ + tmp = toread & (~0x03); + for (i = 0; i < tmp; i += 4) { + ubi32_nand_spi_er_send_rd_addr(0x03, column << 8); + *pbuf++ = io->status1; + column += 4; + } + } else { + /* + * Unaligned read + */ + tmp = toread & (~0x03); + for (i = 0; i < tmp; i += 4) { + ubi32_nand_spi_er_send_rd_addr(0x03, column << 8); + memcpy(pbuf, &io->status1, 4); + column += 4; + } + } + + /* + * Fill in any single bytes + */ + tmp = toread & 0x03; + if (tmp) { + uint8_t *bbuf = pbuf; + uint32_t val; + ubi32_nand_spi_er_send_rd_addr(0x03, column << 8); + val = io->status1; + for (i = 0; i < tmp; i++) { + *bbuf++ = val >> 24; + val <<= 8; + } + } + + len -= toread; + *retlen += toread; + + /* + * For the next page, increment the row and always start at column 0 + */ + column = 0; + row++; + } + + mutex_unlock(&chip->lock); + return retval; +} + +/* + * ubi32_nand_spi_er_write + */ +#define WRITE_NOT_ALIGNED(x) ((x & (device->write_size - 1)) != 0) +static int ubi32_nand_spi_er_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct ubi32_nand_spi_er *chip = mtd->priv; + const struct ubi32_nand_spi_er_device *device = chip->device; + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + uint32_t row; + uint32_t col; + int res = 0; + size_t towrite; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: write block to %llx len %d from %p\n", chip->name, to, len, buf); + + *retlen = 0; + + /* + * nothing to write + */ + if (!len) { + return 0; + } + + /* + * Reject writes which go over the end of the flash + */ + if ((to + len) > mtd->size) { + return -EINVAL; + } + + /* + * buf should be aligned to 16 bits + */ + if ((uint32_t)buf & 0x01) { + return -EINVAL; + } + + /* + * Check to see if everything is page aligned + */ + if (WRITE_NOT_ALIGNED(to) || WRITE_NOT_ALIGNED(len)) { + printk(KERN_NOTICE "ubi32_nand_spi_er_write: Attempt to write non page aligned data\n"); + return -EINVAL; + } + + mutex_lock(&chip->lock); + + io->ctl0 |= IO_XFL_CTL0_MCB_LOCK; + + chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; + + /* + * If the first write is a partial write then write at most the number of + * bytes to get us page aligned and then the remainder will be + * page aligned. The last bit may be a partial page as well. + */ + col = to & (device->page_size - 1); + towrite = device->page_size - col; + if (towrite > len) { + towrite = len; + } + + /* + * Write the data + */ + row = to >> 11; + while (len) { + uint8_t stat; + uint32_t my_towrite; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: write %p to row:%x col:%x len:%x rem:%x\n", chip->name, buf, row, col, towrite, len); + + ubi32_nand_spi_er_write_enable(); + + /* + * Move the data into the cache + */ + my_towrite = towrite; + while (my_towrite) { + uint32_t len = my_towrite; + if (len > 32) { + len = 32; + } + + ubi32_nand_spi_er_write_buf(buf, col); + buf += len; + col += len; + my_towrite -= len; + } + + /* + * Program execute + */ + ubi32_nand_spi_er_send_cmd_addr(0x10, row); + + /* + * Wait + */ + stat = ubi32_nand_spi_er_busywait(); + if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); + + /* + * Chip is stuck? + */ + res = -EIO; + goto done; + } + + if (stat & (1 << 3)) { + res = -EBADMSG; + goto done; + } + + row++; + len -= towrite; + *retlen += towrite; + + /* + * At this point, we are always page aligned so start at column 0. + * Note we may not have a full page to write at the end, hence the + * check if towrite > len. + */ + col = 0; + towrite = device->page_size; + if (towrite > len) { + towrite = len; + } + } + + io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; + + mutex_unlock(&chip->lock); + return res; + +done: + ubi32_nand_spi_er_write_disable(); + + io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; + + mutex_unlock(&chip->lock); + + return res; +} + +/* + * ubi32_nand_spi_er_isbad + */ +static int ubi32_nand_spi_er_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct ubi32_nand_spi_er *chip = mtd->priv; + uint32_t block; + + if (ofs & (chip->device->erase_size - 1)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); + return -EINVAL; + } + + block = ofs >> 17; + + return test_bit(block, chip->bbt); +} + +/* + * ubi32_nand_spi_er_markbad + */ +static int ubi32_nand_spi_er_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct ubi32_nand_spi_er *chip = mtd->priv; + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + uint32_t block; + uint32_t row; + int res = 0; + uint8_t stat; + + if (ofs & (chip->device->erase_size - 1)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); + return -EINVAL; + } + + block = ofs >> 17; + + /* + * If it's already marked bad, no need to mark it + */ + if (test_bit(block, chip->bbt)) { + return 0; + } + + /* + * Mark it in our cache + */ + __set_bit(block, chip->bbt); + + /* + * Write the user bad block mark. If it fails, then we really + * can't do anything about it. + */ + mutex_lock(&chip->lock); + chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; + + ubi32_nand_spi_er_write_enable(); + + /* + * Write the mark + */ + io->ctl0 |= IO_XFL_CTL0_MCB_LOCK; + io->ctl1 &= ~IO_XFL_CTL1_MASK; + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | IO_XFL_CTL1_FC_DATA(6); + io->ctl2 = IO_XFL_CTL2_FC_CMD(0x84); + + asm volatile ( + " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_PORTX_INT_FIFO_TX_RESET)") \n\t" + " pipe_flush 0 \n\t" + + /* + * Move the data into the FIFO + */ + " move.4 "D(IO_TX_FIFO)"(%[port]), %[word1] \n\t" + " move.4 "D(IO_TX_FIFO)"(%[port]), %[word2] \n\t" + + /* + * Kick off the flash command + */ + " bset "D(IO_INT_CLR)"(%[port]), #0, #%%bit("D(IO_XFL_INT_DONE)") \n\t" + " jmpt.t .+4 \n\t" + " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_XFL_INT_START)") \n\t" + + /* + * Wait for the transaction to finish + */ + " btst "D(IO_INT_STATUS)"(%[port]), #%%bit("D(IO_XFL_INT_DONE)") \n\t" + " jmpeq.f .-4 \n\t" + + : + : [word1] "d" (0x0800dead | (UBI32_NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET << 16)), + [word2] "d" (0xbeef0000), + [port] "a" (FLASH_PORT) + : "cc" + ); + + io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; + + /* + * Program execute + */ + row = block << 6; + ubi32_nand_spi_er_send_cmd_addr(0x10, row); + + /* + * Wait + */ + stat = ubi32_nand_spi_er_busywait(); + if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); + + /* + * Chip is stuck? + */ + res = -EIO; + goto done; + } + + if (stat & (1 << 3)) { + res = -EBADMSG; + } + +done: + ubi32_nand_spi_er_write_disable(); + + mutex_unlock(&chip->lock); + + return res; +} + +/* + * ubi32_nand_spi_er_read_bbt + */ +static int ubi32_nand_spi_er_read_bbt(struct ubi32_nand_spi_er *chip) +{ + int j; + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + + for (j = 0; j < chip->device->blocks; j++) { + unsigned short row = j << 6; + uint8_t stat; + + /* + * Read Page + */ + ubi32_nand_spi_er_send_cmd_addr(0x13, row); + + /* + * Wait + */ + stat = ubi32_nand_spi_er_busywait(); + if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); + + /* + * Chip is stuck? + */ + return -EIO; + } + + /* + * Check factory bad block mark + */ + ubi32_nand_spi_er_send_rd_addr(0x03, 0x080000); + + if ((io->status1 >> 24) != 0xFF) { + chip->nbb++; + __set_bit(j, chip->bbt); + continue; + } + + ubi32_nand_spi_er_send_rd_addr(0x03, 0x080000 | (UBI32_NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET << 8)); + if (io->status1 == 0xdeadbeef) { + chip->nbb++; + __set_bit(j, chip->bbt); + } + } + +#if defined(CONFIG_MTD_DEBUG) && (MTD_DEBUG_LEVEL3 <= CONFIG_MTD_DEBUG_VERBOSE) + printk("%s: Bad Block Table:", chip->name); + for (j = 0; j < chip->device->blocks; j++) { + if ((j % 64) == 0) { + printk("\n%s: block %03x: ", chip->name, j); + } + printk("%c", test_bit(j, chip->bbt) ? 'X' : '.'); + } + printk("\n%s: Bad Block Numbers: ", chip->name); + for (j = 0; j < chip->device->blocks; j++) { + if (test_bit(j, chip->bbt)) { + printk("%x ", j); + } + } + printk("\n"); +#endif + + return 0; +} + +#ifndef MODULE +/* + * Called at boot time: + * + * ubi32_nand_spi_er=read_only + * if read_only specified then do not unlock device + */ +static int __init ubi32_nand_spi_er_setup(char *str) +{ + if (str && (strncasecmp(str, "read_only", 9) == 0)) { + read_only = 1; + } + return 0; +} + +__setup("ubi32_nand_spi_er=", ubi32_nand_spi_er_setup); +#endif + +/* + * ubi32_nand_spi_er_probe + * Detect and initialize ubi32_nand_spi_er device. + */ +static int __devinit ubi32_nand_spi_er_probe(struct platform_device *pdev) +{ + uint32_t i; + uint32_t id; + int res; + size_t bbt_bytes; + struct ubi32_nand_spi_er *chip; + const struct ubi32_nand_spi_er_device *device; + struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; + + /* + * Reset + */ + for (i = 0; i < 2; i++) { + io->ctl1 &= ~IO_XFL_CTL1_MASK; + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD); + io->ctl2 = IO_XFL_CTL2_FC_CMD(0xFF); + FLASH_COMMAND_EXEC(io); + udelay(250); + } + udelay(1000); + + /* + * Read out ID + */ + io->ctl1 &= ~IO_XFL_CTL1_MASK; + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_RD) | IO_XFL_CTL1_FC_DATA(2) | + IO_XFL_CTL1_FC_ADDR; + io->ctl2 = IO_XFL_CTL2_FC_CMD(0x9F); + FLASH_COMMAND_EXEC(io); + + id = io->status1 >> 16; + device = ubi32_nand_spi_er_devices; + for (i = 0; i < ARRAY_SIZE(ubi32_nand_spi_er_devices); i++) { + if (device->id == id) { + break; + } + device++; + } + if (i == ARRAY_SIZE(ubi32_nand_spi_er_devices)) { + return -ENODEV; + } + + /* + * Initialize our chip structure + */ + bbt_bytes = DIV_ROUND_UP(device->blocks, BITS_PER_BYTE); + chip = kzalloc(sizeof(struct ubi32_nand_spi_er) + bbt_bytes, GFP_KERNEL); + if (!chip) { + return -ENOMEM; + } + snprintf(chip->name, sizeof(chip->name), "%s", device->name); + + chip->device = device; + chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; + + mutex_init(&chip->lock); + + chip->mtd.type = MTD_NANDFLASH; + chip->mtd.flags = MTD_WRITEABLE; + + /* + * #blocks * block size * n blocks + */ + chip->mtd.size = device->blocks * device->pages_per_block * device->page_size; + chip->mtd.erasesize = device->erase_size; + + /* + * 1 page, optionally we can support partial write (512) + */ + chip->mtd.writesize = device->write_size; + chip->mtd.name = device->name; + chip->mtd.erase = ubi32_nand_spi_er_erase; + chip->mtd.read = ubi32_nand_spi_er_read; + chip->mtd.write = ubi32_nand_spi_er_write; + chip->mtd.block_isbad = ubi32_nand_spi_er_isbad; + chip->mtd.block_markbad = ubi32_nand_spi_er_markbad; + chip->mtd.priv = chip; + + /* + * Cache the bad block table + */ + res = ubi32_nand_spi_er_read_bbt(chip); + if (res) { + kfree(chip); + return res; + } + + /* + * Un/lock the chip + */ + io->ctl0 |= IO_XFL_CTL0_MCB_LOCK; + io->ctl1 &= ~IO_XFL_CTL1_MASK; + io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | IO_XFL_CTL1_FC_DATA(2); + io->ctl2 = IO_XFL_CTL2_FC_CMD(0x1F); + + if (read_only) { + i = 0xa0380000; + } else { + i = 0xa0000000; + } + asm volatile ( + " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_PORTX_INT_FIFO_TX_RESET)") \n\t" + " pipe_flush 0 \n\t" + + /* + * Move the data into the FIFO + */ + " move.4 "D(IO_TX_FIFO)"(%[port]), %[word1] \n\t" + + /* + * Kick off the flash command + */ + " bset "D(IO_INT_CLR)"(%[port]), #0, #%%bit("D(IO_XFL_INT_DONE)") \n\t" + " jmpt.t .+4 \n\t" + " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_XFL_INT_START)") \n\t" + + /* + * Wait for the transaction to finish + */ + " btst "D(IO_INT_STATUS)"(%[port]), #%%bit("D(IO_XFL_INT_DONE)") \n\t" + " jmpeq.f .-4 \n\t" + + : + : [word1] "d" (i), + [port] "a" (FLASH_PORT) + : "cc" + ); + io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; + + dev_set_drvdata(&pdev->dev, chip); + + printk(KERN_INFO "%s: added device size: %u KBytes %lu bad blocks %s\n", chip->mtd.name, DIV_ROUND_UP(chip->mtd.size, 1024), chip->nbb, read_only ? "[read only]" : ""); + return add_mtd_device(&chip->mtd); +} + +/* + * ubi32_nand_spi_er_remove + */ +static int __devexit ubi32_nand_spi_er_remove(struct platform_device *pdev) +{ + struct ubi32_nand_spi_er *chip = dev_get_drvdata(&pdev->dev); + int status; + + DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", chip->name); + + status = del_mtd_device(&chip->mtd); + if (status == 0) { + kfree(chip); + } + + dev_set_drvdata(&pdev->dev, NULL); + return status; +} + +static struct platform_device *ubi32_nand_spi_er_device; + +static struct platform_driver ubi32_nand_spi_er_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + + .probe = ubi32_nand_spi_er_probe, + .remove = ubi32_nand_spi_er_remove, +}; + +/* + * ubi32_nand_spi_er_init + */ +static int __init ubi32_nand_spi_er_init(void) +{ + int ret; + + ret = platform_driver_register(&ubi32_nand_spi_er_driver); + + if (ret) { + return ret; + } + + ubi32_nand_spi_er_device = platform_device_alloc(DRIVER_NAME, 0); + if (!ubi32_nand_spi_er_device) { + return -ENOMEM; + } + + ret = platform_device_add(ubi32_nand_spi_er_device); + if (ret) { + platform_device_put(ubi32_nand_spi_er_device); + platform_driver_unregister(&ubi32_nand_spi_er_driver); + } + + return ret; +} +module_init(ubi32_nand_spi_er_init); + +/* + * ubi32_nand_spi_er_exit + */ +static void __exit ubi32_nand_spi_er_exit(void) +{ + platform_device_unregister(ubi32_nand_spi_er_device); + platform_driver_unregister(&ubi32_nand_spi_er_driver); +} +module_exit(ubi32_nand_spi_er_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick Tjin"); +MODULE_DESCRIPTION("MTD ubi32_nand_spi_er driver for ubicom32 SPI flash controller."); diff --git a/target/linux/ubicom32/files/drivers/net/ubi32-eth.c b/target/linux/ubicom32/files/drivers/net/ubi32-eth.c new file mode 100644 index 0000000000..e6c7392e06 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/net/ubi32-eth.c @@ -0,0 +1,760 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +/* + * ubi32_eth.c + * Ethernet driver for Ip5k/Ip7K + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UBICOM32_USE_NAPI /* define this to use NAPI instead of tasklet */ +//#define UBICOM32_USE_POLLING /* define this to use polling instead of interrupt */ +#include "ubi32-eth.h" + +/* + * TODO: + * mac address from flash + * multicast filter + * ethtool support + * sysfs support + * skb->nrfrag support + * ioctl + * monitor phy status + */ + +extern int ubi32_ocm_skbuf_max, ubi32_ocm_skbuf, ubi32_ddr_skbuf; +static const char *eth_if_name[UBI32_ETH_NUM_OF_DEVICES] = + {"eth_lan", "eth_wan"}; +static struct net_device *ubi32_eth_devices[UBI32_ETH_NUM_OF_DEVICES] = + {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'}}; + +#if (defined(CONFIG_ZONE_DMA) && defined(CONFIG_UBICOM32_OCM_FOR_SKB)) +static inline struct sk_buff *ubi32_alloc_skb_ocm(struct net_device *dev, unsigned int length) +{ + return __dev_alloc_skb(length, GFP_ATOMIC | __GFP_NOWARN | __GFP_NORETRY | GFP_DMA); +} +#endif + +static inline struct sk_buff *ubi32_alloc_skb(struct net_device *dev, unsigned int length) +{ + return __dev_alloc_skb(length, GFP_ATOMIC | __GFP_NOWARN); +} + +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))) { + dev->stats.tx_errors++; + } else { + dev->stats.tx_packets++; + dev->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->regs->status & UBI32_ETH_VP_STATUS_TX_Q_FULL)) { + spin_lock(&priv->lock); + if (priv->regs->status & UBI32_ETH_VP_STATUS_TX_Q_FULL) { + priv->regs->status &= ~UBI32_ETH_VP_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; + + int extra_reserve_adj; + int extra_alloc = UBI32_ETH_RESERVE_SPACE + UBI32_ETH_TRASHED_MEMORY; + int replenish_cnt, count = 0; + int replenish_max = RX_DMA_MAX_QUEUE_SIZE; +#if (defined(CONFIG_ZONE_DMA) && defined(CONFIG_UBICOM32_OCM_FOR_SKB)) + if (likely(dev == ubi32_eth_devices[0])) + replenish_max = min(ubi32_ocm_skbuf_max, RX_DMA_MAX_QUEUE_SIZE);; +#endif + + 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))) { + dev->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; + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; +#ifndef UBICOM32_USE_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) { +#if (defined(CONFIG_ZONE_DMA) && defined(CONFIG_UBICOM32_OCM_FOR_SKB)) + /* + * black magic for perforamnce: + * Try to allocate skb from OCM only for first Ethernet I/F. + * Also limit number of RX buffers to 21 due to limited OCM. + */ + if (likely(dev == ubi32_eth_devices[0])) { + do { + skb = ubi32_alloc_skb_ocm(dev, RX_BUF_SIZE + extra_alloc); + if (!skb) { + break; + } + /* set up dma descriptor */ + ubi32_ocm_skbuf++; + desc = (struct ubi32_eth_dma_desc *)skb->cb; + extra_reserve_adj = + ((u32)skb->data + UBI32_ETH_RESERVE_SPACE + ETH_HLEN) & + (CACHE_LINE_SIZE - 1); + skb_reserve(skb, UBI32_ETH_RESERVE_SPACE - extra_reserve_adj); + 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; + } while (--replenish_cnt > 0); + } +#endif + + while (replenish_cnt-- > 0) { + skb = ubi32_alloc_skb(dev, RX_BUF_SIZE + extra_alloc); + if (!skb) { + priv->vp_stats.rx_alloc_err++; + break; + } + /* set up dma descriptor */ + ubi32_ddr_skbuf++; + desc = (struct ubi32_eth_dma_desc *)skb->cb; + extra_reserve_adj = + ((u32)skb->data + UBI32_ETH_RESERVE_SPACE + ETH_HLEN) & + (CACHE_LINE_SIZE - 1); + skb_reserve(skb, UBI32_ETH_RESERVE_SPACE - extra_reserve_adj); + 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 UBICOM32_USE_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); + + if (count < budget) { + napi_complete(napi); + 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)) { + if (napi_reschedule(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, UBI32_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(UBICOM32_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 UBICOM32_USE_NAPI + napi_schedule(&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 UBICOM32_USE_NAPI + if (napi_schedule_prep(&priv->napi)) { + priv->regs->int_mask = 0; + __napi_schedule(&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 UBICOM32_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 UBICOM32_USE_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 UBICOM32_USE_POLLING + free_irq(dev->irq, dev); +#endif + netif_stop_queue(dev); /* can't transmit any more */ +#ifdef UBICOM32_USE_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 UBICOM32_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)) { + if (!(priv->regs->status & UBI32_ETH_VP_STATUS_TX_Q_FULL)) { + spin_lock(&priv->lock); + if (!(priv->regs->status & UBI32_ETH_VP_STATUS_TX_Q_FULL)) { + priv->regs->status |= UBI32_ETH_VP_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 UBICOM32_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 NETDEV_TX_OK; +} + +/* + * Deal with a transmit timeout. + */ +static void ubi32_eth_tx_timeout (struct net_device *dev) +{ + struct ubi32_eth_private *priv = netdev_priv(dev); + dev->stats.tx_errors++; + priv->regs->int_mask |= UBI32_ETH_VP_INT_TX; +#ifndef UBICOM32_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) +{ + struct ubi32_eth_private *priv = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(rq); + + printk(KERN_INFO "ioctl %s, %d\n", dev->name, cmd); + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 0; + break; + + case SIOCGMIIREG: + if ((data->reg_num & 0x1F) == MII_BMCR) { + /* Make up MII control register value from what we know */ + data->val_out = 0x0000 + | ((priv->regs->status & UBI32_ETH_VP_STATUS_DUPLEX) + ? BMCR_FULLDPLX : 0) + | ((priv->regs->status & UBI32_ETH_VP_STATUS_SPEED100) + ? BMCR_SPEED100 : 0) + | ((priv->regs->status & UBI32_ETH_VP_STATUS_SPEED1000) + ? BMCR_SPEED1000 : 0); + } else if ((data->reg_num & 0x1F) == MII_BMSR) { + /* Make up MII status register value from what we know */ + data->val_out = + (BMSR_100FULL|BMSR_100HALF|BMSR_10FULL|BMSR_10HALF) + | ((priv->regs->status & UBI32_ETH_VP_STATUS_LINK) + ? BMSR_LSTATUS : 0); + } else { + return -EIO; + } + break; + + case SIOCSMIIREG: + return -EOPNOTSUPP; + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* + * Return statistics to the caller + */ +static struct net_device_stats *ubi32_eth_get_stats(struct net_device *dev) +{ + return &dev->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 "fail 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; + + /* + * This just fill in some default Ubicom MAC address + */ + memcpy(dev->dev_addr, mac_addr[i], ETH_ALEN); + memset(dev->broadcast, 0xff, 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 UBICOM32_USE_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 feature:0x%lx\n", + dev->name, priv->regs, eth_node->dn.sendirq, dev->irq, dev->features); + } + + if (err) { + ubi32_eth_cleanup(); + return err; + } + + if (!ubi32_eth_devices[0] && !ubi32_eth_devices[1]) { + return -ENODEV; + } + +#if defined(UBICOM32_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"); diff --git a/target/linux/ubicom32/files/drivers/net/ubi32-eth.h b/target/linux/ubicom32/files/drivers/net/ubi32-eth.h new file mode 100644 index 0000000000..c25500143a --- /dev/null +++ b/target/linux/ubicom32/files/drivers/net/ubi32-eth.h @@ -0,0 +1,132 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _UBI32_ETH_H +#define _UBI32_ETH_H + +#include + +#define UBI32_ETH_NUM_OF_DEVICES 2 + +/* + * 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 at least one cache line to align Ethernet + * and IP header to cache line. + * Note that the TIO expects a CACHE_LINE_SIZE - ETH_HLEN aligned Ethernet + * header, while satisfies NET_IP_ALIGN (= 2) automatically. + * (NET_SKB_PAD is 16, NET_IP_ALIGN is 2, CACHE_LINE_SIZE is 32). + * You can add more space by making UBI32_ETH_RESERVE_EXTRA != 0. + */ +#define UBI32_ETH_RESERVE_EXTRA (1 * CACHE_LINE_SIZE) +#define UBI32_ETH_RESERVE_SPACE (UBI32_ETH_RESERVE_EXTRA + 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 ubi32_eth_vp_stats vp_stats; + spinlock_t lock; +#ifdef UBICOM32_USE_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_SPEED100 (0x1<<1) +#define UBI32_ETH_VP_STATUS_SPEED1000 (0x1<<2) +#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_VP_STATUS_TX_Q_FULL (1<<8) + +#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 UBI32_TX_BOUND TX_DMA_RING_SIZE +#define UBI32_RX_BOUND 64 +#define UBI32_ETH_NAPI_WEIGHT 64 /* for GigE */ +#endif diff --git a/target/linux/ubicom32/files/drivers/serial/ubi32_mailbox.c b/target/linux/ubicom32/files/drivers/serial/ubi32_mailbox.c new file mode 100644 index 0000000000..fc0d6d21b6 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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"); diff --git a/target/linux/ubicom32/files/drivers/serial/ubi32_serdes.c b/target/linux/ubicom32/files/drivers/serial/ubi32_serdes.c new file mode 100644 index 0000000000..79037da494 --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#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 "); +MODULE_DESCRIPTION("Ubicom generic serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(UBI32_SERDES_MAJOR); +MODULE_ALIAS("platform:ubi32_serdes"); diff --git a/target/linux/ubicom32/files/drivers/serial/ubi32_uarttio.c b/target/linux/ubicom32/files/drivers/serial/ubi32_uarttio.c new file mode 100644 index 0000000000..7aa3742001 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/serial/ubi32_uarttio.c @@ -0,0 +1,1172 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; + static int probe_result; + + /* + * We only want to be probed once, we could be probed twice + * for example if we are used as a console + */ + if (probed) { + return probe_result; + } + probed = 1; + + /* + * Extract the TIO name from the setup string + */ + while (*str) { + if (*str == ',') { + *str++ = 0; + break; + } + str++; + } + + if (!*str) { + probe_result = -EINVAL; + return -EINVAL; + } + + uart_node = (struct uarttio_node *)devtree_find_node(utio_ports_param); + if (!uart_node) { + probe_result = -ENODEV; + 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) { + ubi32_uarttio_cleanup(); + probe_result = ret; + return ret; + } + + ubi32_uarttio_uart_driver.nr = uarttio_nports; + + return 0; +} + +#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 "); +MODULE_DESCRIPTION("Ubicom serial virtual peripherial driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(UBI32_UARTTIO_MAJOR); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/target/linux/ubicom32/files/drivers/spi/spi_ubicom32_gpio.c b/target/linux/ubicom32/files/drivers/spi/spi_ubicom32_gpio.c new file mode 100644 index 0000000000..c41018a26d --- /dev/null +++ b/target/linux/ubicom32/files/drivers/spi/spi_ubicom32_gpio.c @@ -0,0 +1,267 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#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 + +/* + * 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"); diff --git a/target/linux/ubicom32/files/drivers/uio/uio_ubicom32ring.c b/target/linux/ubicom32/files/drivers/uio/uio_ubicom32ring.c new file mode 100644 index 0000000000..654ac4ced8 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/uio/uio_ubicom32ring.c @@ -0,0 +1,288 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#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); diff --git a/target/linux/ubicom32/files/drivers/usb/musb/ubi32_usb.c b/target/linux/ubicom32/files/drivers/usb/musb/ubi32_usb.c new file mode 100644 index 0000000000..d89e00416b --- /dev/null +++ b/target/linux/ubicom32/files/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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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; +} diff --git a/target/linux/ubicom32/files/drivers/video/backlight/ubicom32bl.c b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32bl.c new file mode 100644 index 0000000000..99538c341c --- /dev/null +++ b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32bl.c @@ -0,0 +1,399 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 (PA5) + */ + UBICOM32_IO_PORT(RC)->ctl0 &= ~0xFFFF; + UBICOM32_IO_PORT(RC)->ctl0 |= pwm_cfg; + UBICOM32_IO_PORT(RC)->ctl1 = ud->pdata->pwm_period << 16; + + /* + * If the port function is not set, set it to GPIO/PWM + */ + if (!UBICOM32_IO_PORT(RA)->function) { + UBICOM32_IO_PORT(RA)->function = 3; + } + break; + + case 1: + /* + * Channel 1 is in the upper half of PORT C ctl0 and ctl2 (PE4) + */ + UBICOM32_IO_PORT(RC)->ctl0 &= ~0xFFFF0000; + UBICOM32_IO_PORT(RC)->ctl0 |= (pwm_cfg << 16); + UBICOM32_IO_PORT(RC)->ctl2 = ud->pdata->pwm_period << 16; + + /* + * If the port function is not set, set it to GPIO/ExtIOInt + */ + if (!UBICOM32_IO_PORT(RE)->function) { + UBICOM32_IO_PORT(RE)->function = 3; + } + break; + + case 2: + /* + * Channel 2 is in PORT H ctl0 and ctl1 (PD0) + */ + UBICOM32_IO_PORT(RH)->ctl0 &= ~0xFFFF0000; + UBICOM32_IO_PORT(RH)->ctl0 = pwm_cfg; + UBICOM32_IO_PORT(RH)->ctl1 = ud->pdata->pwm_period << 16; + + /* + * If the port function is not set, set it to GPIO + */ + if (!UBICOM32_IO_PORT(RD)->function) { + UBICOM32_IO_PORT(RD)->function = 3; + } + 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"); diff --git a/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.c b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.c new file mode 100644 index 0000000000..e4f8c713e0 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.c @@ -0,0 +1,372 @@ +/* + * drivers/video/ubicom32lcd.c + * LCD initilization 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 . + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ubicom32lcd.h" + +#define DRIVER_NAME "ubicom32lcd" + +struct ubicom32lcd_data { + const struct ubicom32lcd_panel *panel; + + int pin_cs; + int pin_rd; + int pin_rs; + int pin_wr; + int pin_reset; + struct ubicom32_io_port *port_data; + int data_shift; +}; + +/* + * ubicom32lcd_write + * Performs a write cycle on the bus (assumes CS asserted, RD & WR set) + */ +static void ubicom32lcd_write(struct ubicom32lcd_data *ud, int command, u16 data) +{ + if (command) { + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_rs); + } else { + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); + } + + asm volatile ( + "or.4 4(%[port]), 4(%[port]), %[mask] \n\t" + "not.4 %[mask], %[mask] \n\t" + "and.4 8(%[port]), 8(%[port]), %[mask] \n\t" + "or.4 8(%[port]), 8(%[port]), %[cmd] \n\t" + : + : [port] "a" (ud->port_data), + [mask] "d" (0xFFFF << ud->data_shift), + [cmd] "d" (data << ud->data_shift) + : "cc" + ); + + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_wr); + + //ndelay(50); + udelay(1); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); + + udelay(1); + //ndelay(50); +} + +/* + * ubicom32lcd_read_data + * Performs a read cycle on the bus (assumes CS asserted, RD & WR set) + */ +static u16 ubicom32lcd_read_data(struct ubicom32lcd_data *ud) +{ + u32_t data; + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); + + asm volatile ( + "and.4 4(%[port]), 4(%[port]), %[mask]\n\t" + : + : [port] "a" (ud->port_data), + [mask] "d" (~(0xFFFF << ud->data_shift)) + : "cc" + ); + + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_rd); + + ndelay(300); + + asm volatile ( + "lsr.4 %[data], 12(%[port]), %[shamt] \n\t" + "and.4 %[data], %[data], %[mask] \n\t" + : [data] "=d" (data) + : [port] "a" (ud->port_data), + [mask] "d" (0xFFFF), + [shamt] "d" (ud->data_shift) + : "cc" + ); + + ndelay(200); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); + + ndelay(500); + + return data; +} + +/* + * ubicom32lcd_execute + * Executes a script for performing operations on the LCD (assumes CS set) + */ +static void ubicom32lcd_execute(struct ubicom32lcd_data *ud, const struct ubicom32lcd_step *script) +{ + while (1) { + switch (script->op) { + case LCD_STEP_CMD: + ubicom32lcd_write(ud, 1, script->cmd); + break; + + case LCD_STEP_DATA: + ubicom32lcd_write(ud, 0, script->data); + break; + + case LCD_STEP_CMD_DATA: + ubicom32lcd_write(ud, 1, script->cmd); + ubicom32lcd_write(ud, 0, script->data); + break; + + case LCD_STEP_SLEEP: + udelay(script->data); + break; + + case LCD_STEP_DONE: + return; + } + script++; + } +} + +/* + * ubicom32lcd_goto + * Places the gram pointer at a specific X, Y address + */ +static void ubicom32lcd_goto(struct ubicom32lcd_data *ud, int x, int y) +{ + ubicom32lcd_write(ud, 1, ud->panel->horz_reg); + ubicom32lcd_write(ud, 0, x); + ubicom32lcd_write(ud, 1, ud->panel->vert_reg); + ubicom32lcd_write(ud, 0, y); + ubicom32lcd_write(ud, 1, ud->panel->gram_reg); +} + +/* + * ubicom32lcd_panel_init + * Initializes the lcd panel. + */ +static int ubicom32lcd_panel_init(struct ubicom32lcd_data *ud) +{ + u16 id; + + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_reset); + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_reset); + UBICOM32_GPIO_ENABLE(ud->pin_reset); + + asm volatile ( + "or.4 0x50(%[port]), 0x50(%[port]), %[mask] \n\t" + "not.4 %[mask], %[mask] \n\t" + "and.4 0x04(%[port]), 0x04(%[port]), %[mask] \n\t" + : + : [port] "a" (ud->port_data), + [mask] "d" (0xFFFF << ud->data_shift) + : "cc" + ); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); + + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_rs); + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_rd); + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_wr); + UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_cs); + + UBICOM32_GPIO_ENABLE(ud->pin_rs); + UBICOM32_GPIO_ENABLE(ud->pin_rd); + UBICOM32_GPIO_ENABLE(ud->pin_wr); + UBICOM32_GPIO_ENABLE(ud->pin_cs); + + udelay(20); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_reset); + + udelay(20); + + UBICOM32_GPIO_SET_PIN_LOW(ud->pin_cs); + + id = ubicom32lcd_read_data(ud); + + /* + * We will try to figure out what kind of panel we have if we were not told. + */ + if (!ud->panel) { + const struct ubicom32lcd_panel **p = ubicom32lcd_panels; + while (*p) { + if ((*p)->id && ((*p)->id == id)) { + break; + } + p++; + } + if (!*p) { + printk(KERN_WARNING DRIVER_NAME ":Could not find compatible panel, id=%x\n", id); + return -ENODEV; + } + ud->panel = *p; + } + + /* + * Make sure panel ID matches if we were supplied a panel type + */ + if (ud->panel->id && (ud->panel->id != id)) { + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); + + return -ENODEV; + } + + ubicom32lcd_execute(ud, ud->panel->init_seq); + + ubicom32lcd_goto(ud, 0, 0); + + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); + UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); + + printk(KERN_INFO DRIVER_NAME ": Initialized panel %s\n", ud->panel->desc); + + return 0; +} + +/* + * ubicom32lcd_probe + */ +static int ubicom32lcd_probe(struct platform_device *pdev) +{ + const struct ubicom32lcd_platform_data *pdata = pdev->dev.platform_data; + struct ubicom32lcd_data *ud; + int retval; + + /* + * Allocate our private data + */ + ud = kzalloc(sizeof(struct ubicom32lcd_data), GFP_KERNEL); + if (!ud) { + return -ENOMEM; + } + + if (pdata) { + ud->pin_cs = pdata->pin_cs; + ud->pin_rd = pdata->pin_rd; + ud->pin_wr = pdata->pin_wr; + ud->pin_rs = pdata->pin_rs; + ud->pin_reset = pdata->pin_reset; + ud->port_data = pdata->port_data; + ud->data_shift = pdata->data_shift; + } else { + /* + * Defaults + */ + ud->pin_cs = GPIO_RD_4; + ud->pin_rd = GPIO_RD_5; + ud->pin_rs = GPIO_RD_3; + ud->pin_wr = GPIO_RD_2; + ud->pin_reset = GPIO_RD_7; + ud->port_data = (struct ubicom32_io_port *)RI; + ud->data_shift = 0; + } + + /* + * Initialize the display + */ + retval = ubicom32lcd_panel_init(ud); + if (retval) { + kfree(ud); + return retval; + } + + printk(KERN_INFO DRIVER_NAME ": LCD initialized\n"); + + return 0; +} + +/* + * ubicom32lcd_remove + */ +static int __exit ubicom32lcd_remove(struct platform_device *pdev) +{ + struct ubicom32lcd_data *ud = platform_get_drvdata(pdev); + + kfree(ud); + + return 0; +} + +static struct platform_driver ubicom32lcd_driver = { + .probe = ubicom32lcd_probe, + .remove = ubicom32lcd_remove, + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + + .remove = __exit_p(ubicom32lcd_remove), +}; + +static struct platform_device *ubicom32lcd_device; + +/* + * ubicom32lcd_init + */ +static int __init ubicom32lcd_init(void) +{ + int res; + + res = platform_driver_register(&ubicom32lcd_driver); + if (res == 0) { + ubicom32lcd_device = platform_device_alloc(DRIVER_NAME, 0); + if (ubicom32lcd_device) { + res = platform_device_add(ubicom32lcd_device); + } else { + res = -ENOMEM; + } + if (res) { + platform_device_put(ubicom32lcd_device); + platform_driver_unregister(&ubicom32lcd_driver); + } + } + return res; +} +module_init(ubicom32lcd_init); + +/* + * ubicom32lcd_exit + */ +static void __exit ubicom32lcd_exit(void) +{ + platform_device_unregister(ubicom32lcd_device); + platform_driver_unregister(&ubicom32lcd_driver); +} +module_exit(ubicom32lcd_exit); + +MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); +MODULE_DESCRIPTION("Ubicom32 LCD driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.h b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.h new file mode 100644 index 0000000000..07650ba9e3 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcd.h @@ -0,0 +1,546 @@ +/* + * ubicom32lcd.h + * Ubicom32 lcd panel drivers + * + * (C) Copyright 2009, Ubicom, Inc. + * + * This file is part of the Ubicom32 Linux Kernel Port. + * + * This Ubicom32 library is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This Ubicom32 library is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . + */ + +#ifndef _UBICOM32LCD_H_ +#define _UBICOM32LCD_H_ + +enum ubicom32lcd_op { + /* + * Sleep for (data) ms + */ + LCD_STEP_SLEEP, + + /* + * Execute write of command + */ + LCD_STEP_CMD, + + /* + * Execute write of data + */ + LCD_STEP_DATA, + + /* + * Execute write of command/data + */ + LCD_STEP_CMD_DATA, + + /* + * Script done + */ + LCD_STEP_DONE, +}; + +struct ubicom32lcd_step { + enum ubicom32lcd_op op; + u16 cmd; + u16 data; +}; + +struct ubicom32lcd_panel { + const struct ubicom32lcd_step *init_seq; + const char *desc; + + u32 xres; + u32 yres; + u32 stride; + u32 flags; + + u16 id; + u16 horz_reg; + u16 vert_reg; + u16 gram_reg; +}; + +#ifdef CONFIG_LCD_UBICOM32_CFAF240320KTTS +static const struct ubicom32lcd_step cfaf240320ktts_init_0[] = { + {LCD_STEP_CMD_DATA, 0x0001, 0x0000,}, // Driver Output Control Register (R01h) Page 14, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0002, 0x0700,}, // LCD Driving Waveform Control (R02h) Page 15, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0003, 0x50A0,}, // Entry Mode (R03h) 0 degrees + {LCD_STEP_CMD_DATA, 0x0004, 0x0000,}, // Scaling Control register (R04h) Page 16, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0008, 0x0207,}, // Display Control 2 (R08h) Page 17, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0009, 0x0000,}, // Display Control 3 (R09h) Page 18, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x000A, 0x0000,}, // Frame Cycle Control (R0Ah) Page 19, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x000C, 0x0000,}, // External Display Interface Control 1 (R0Ch) Page 20, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x000D, 0x0000,}, // Frame Maker Position (R0Dh) Page 21, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x000F, 0x0000,}, // External Display Interface Control 2 (R0Fh) Page 21, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0010, 0x0000,}, // Power Control 1 (R10h) Page 22, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0011, 0x0007,}, // Power Control 2 (R11h) Page 23, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0012, 0x0000,}, // Power Control 3 (R12h) Page 24, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0013, 0x0000,}, // Power Control 4 (R13h) Page 25, SPFD5408B Datasheet + {LCD_STEP_SLEEP, 0, 200}, + {LCD_STEP_CMD_DATA, 0x0007, 0x0101,}, // Display Control (R07h) Page 16, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0010, 0x12B0,}, // Power Control 1 (R10h) Page 22, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0011, 0x0007,}, // Power Control 2 (R11h) Page 23, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0012, 0x01BB,}, // Power Control 3 (R12h) Page 24, SPFD5408B Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0013, 0x1300,}, // Power Control 4 (R13h) Page 25, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0029, 0x0010,}, // NVM read data 2 (R29h) Page 30, SPFD5408B Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0030, 0x000A,}, // Gamma Control 1 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0031, 0x1326,}, // Gamma Control 2 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0032, 0x0A29,}, // Gamma Control 3 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0033, 0x290A,}, // Gamma Control 4 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0034, 0x2613,}, // Gamma Control 5 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0035, 0x0A0A,}, // Gamma Control 6 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0036, 0x1E03,}, // Gamma Control 7 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0037, 0x031E,}, // Gamma Control 8 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0038, 0x0706,}, // Gamma Control 9 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0039, 0x0303,}, // Gamma Control 10 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003A, 0x0E04,}, // Gamma Control 11 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003B, 0x0E01,}, // Gamma Control 12 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003C, 0x010E,}, // Gamma Control 13 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003D, 0x040E,}, // Gamma Control 14 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003E, 0x0303,}, // Gamma Control 15 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003F, 0x0607,}, // Gamma Control 16 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0050, 0x0000,}, // Window Horizontal RAM Address Start (R50h) Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0051, 0x00EF,}, // Window Horizontal RAM Address End (R51h) Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0052, 0x0000,}, // Window Vertical RAM Address Start (R52h) Page 33, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0053, 0x013F,}, // Window Vertical RAM Address End (R53h) Page 33, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0060, 0x2700,}, // Driver Output Control (R60h) Page 33, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0061, 0x0001,}, // Driver Output Control (R61h) Page 35, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x006A, 0x0000,}, // Vertical Scroll Control (R6Ah) Page 35, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0080, 0x0000,}, // Display Position - Partial Display 1 (R80h) Page 35, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0081, 0x0000,}, // RAM Address Start - Partial Display 1 (R81h) Page 35, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0082, 0x0000,}, // RAM Address End - Partial Display 1 (R82h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0083, 0x0000,}, // Display Position - Partial Display 2 (R83h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0084, 0x0000,}, // RAM Address Start - Partial Display 2 (R84h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0085, 0x0000,}, // RAM Address End - Partial Display 2 (R85h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0090, 0x0010,}, // Panel Interface Control 1 (R90h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0092, 0x0000,}, // Panel Interface Control 2 (R92h) Page 37, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0093, 0x0103,}, // Panel Interface control 3 (R93h) Page 38, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0095, 0x0210,}, // Panel Interface control 4 (R95h) Page 38, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0097, 0x0000,}, // Panel Interface Control 5 (R97h) Page 40, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0098, 0x0000,}, // Panel Interface Control 6 (R98h) Page 41, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0007, 0x0173,}, // Display Control (R07h) Page 16, SPFD5408B Datasheet + {LCD_STEP_DONE, 0, 0}, +}; + +const struct ubicom32lcd_panel cfaf240320ktts_0 = { + .desc = "CFAF240320KTTS", + .init_seq = cfaf240320ktts_init_0, + .horz_reg = 0x20, + .vert_reg = 0x21, + .gram_reg = 0x22, + .xres = 240, + .yres = 320, + .stride = 240, + .id = 0x5408, +}; +#endif + +#ifdef CONFIG_LCD_UBICOM32_CFAF240320KTTS_180 +static const struct ubicom32lcd_step cfaf240320ktts_init_180[] = { + {LCD_STEP_CMD_DATA, 0x0001, 0x0000,}, // Driver Output Control Register (R01h) Page 14, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0002, 0x0700,}, // LCD Driving Waveform Control (R02h) Page 15, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0003, 0x5000,}, // Entry Mode (R03h) 180 degrees + {LCD_STEP_CMD_DATA, 0x0004, 0x0000,}, // Scaling Control register (R04h) Page 16, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0008, 0x0207,}, // Display Control 2 (R08h) Page 17, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0009, 0x0000,}, // Display Control 3 (R09h) Page 18, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x000A, 0x0000,}, // Frame Cycle Control (R0Ah) Page 19, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x000C, 0x0000,}, // External Display Interface Control 1 (R0Ch) Page 20, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x000D, 0x0000,}, // Frame Maker Position (R0Dh) Page 21, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x000F, 0x0000,}, // External Display Interface Control 2 (R0Fh) Page 21, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0010, 0x0000,}, // Power Control 1 (R10h) Page 22, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0011, 0x0007,}, // Power Control 2 (R11h) Page 23, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0012, 0x0000,}, // Power Control 3 (R12h) Page 24, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0013, 0x0000,}, // Power Control 4 (R13h) Page 25, SPFD5408B Datasheet + {LCD_STEP_SLEEP, 0, 200}, + {LCD_STEP_CMD_DATA, 0x0007, 0x0101,}, // Display Control (R07h) Page 16, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0010, 0x12B0,}, // Power Control 1 (R10h) Page 22, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0011, 0x0007,}, // Power Control 2 (R11h) Page 23, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0012, 0x01BB,}, // Power Control 3 (R12h) Page 24, SPFD5408B Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0013, 0x1300,}, // Power Control 4 (R13h) Page 25, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0029, 0x0010,}, // NVM read data 2 (R29h) Page 30, SPFD5408B Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0030, 0x000A,}, // Gamma Control 1 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0031, 0x1326,}, // Gamma Control 2 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0032, 0x0A29,}, // Gamma Control 3 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0033, 0x290A,}, // Gamma Control 4 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0034, 0x2613,}, // Gamma Control 5 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0035, 0x0A0A,}, // Gamma Control 6 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0036, 0x1E03,}, // Gamma Control 7 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0037, 0x031E,}, // Gamma Control 8 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0038, 0x0706,}, // Gamma Control 9 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0039, 0x0303,}, // Gamma Control 10 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003A, 0x0E04,}, // Gamma Control 11 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003B, 0x0E01,}, // Gamma Control 12 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003C, 0x010E,}, // Gamma Control 13 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003D, 0x040E,}, // Gamma Control 14 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003E, 0x0303,}, // Gamma Control 15 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x003F, 0x0607,}, // Gamma Control 16 Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0050, 0x0000,}, // Window Horizontal RAM Address Start (R50h) Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0051, 0x00EF,}, // Window Horizontal RAM Address End (R51h) Page 32, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0052, 0x0000,}, // Window Vertical RAM Address Start (R52h) Page 33, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0053, 0x013F,}, // Window Vertical RAM Address End (R53h) Page 33, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0060, 0x2700,}, // Driver Output Control (R60h) Page 33, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0061, 0x0001,}, // Driver Output Control (R61h) Page 35, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x006A, 0x0000,}, // Vertical Scroll Control (R6Ah) Page 35, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0080, 0x0000,}, // Display Position - Partial Display 1 (R80h) Page 35, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0081, 0x0000,}, // RAM Address Start - Partial Display 1 (R81h) Page 35, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0082, 0x0000,}, // RAM Address End - Partial Display 1 (R82h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0083, 0x0000,}, // Display Position - Partial Display 2 (R83h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0084, 0x0000,}, // RAM Address Start - Partial Display 2 (R84h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0085, 0x0000,}, // RAM Address End - Partial Display 2 (R85h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0090, 0x0010,}, // Panel Interface Control 1 (R90h) Page 36, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0092, 0x0000,}, // Panel Interface Control 2 (R92h) Page 37, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0093, 0x0103,}, // Panel Interface control 3 (R93h) Page 38, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0095, 0x0210,}, // Panel Interface control 4 (R95h) Page 38, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0097, 0x0000,}, // Panel Interface Control 5 (R97h) Page 40, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0098, 0x0000,}, // Panel Interface Control 6 (R98h) Page 41, SPFD5408B Datasheet + {LCD_STEP_CMD_DATA, 0x0007, 0x0173,}, // Display Control (R07h) Page 16, SPFD5408B Datasheet + {LCD_STEP_DONE, 0, 0}, +}; + +const struct ubicom32lcd_panel cfaf240320ktts_180 = { + .desc = "CFAF240320KTTS 180", + .init_seq = cfaf240320ktts_init_180, + .horz_reg = 0x20, + .vert_reg = 0x21, + .gram_reg = 0x22, + .xres = 240, + .yres = 320, + .stride = 240, + .id = 0x5408, +}; +#endif + +#ifdef CONFIG_LCD_UBICOM32_TFT2N0369E_P +static const struct ubicom32lcd_step tft2n0369ep_init[] = { + {LCD_STEP_CMD_DATA, 0x0028, 0x0006}, + {LCD_STEP_CMD_DATA, 0x0000, 0x0001}, + {LCD_STEP_SLEEP, 0, 15}, + {LCD_STEP_CMD_DATA, 0x002B, 0x9532}, + {LCD_STEP_CMD_DATA, 0x0003, 0xAAAC}, + {LCD_STEP_CMD_DATA, 0x000C, 0x0002}, + {LCD_STEP_CMD_DATA, 0x000D, 0x000A}, + {LCD_STEP_CMD_DATA, 0x000E, 0x2C00}, + {LCD_STEP_CMD_DATA, 0x001E, 0x00AA}, + {LCD_STEP_CMD_DATA, 0x0025, 0x8000}, + {LCD_STEP_SLEEP, 0, 15}, + {LCD_STEP_CMD_DATA, 0x0001, 0x2B3F}, + {LCD_STEP_CMD_DATA, 0x0002, 0x0600}, + {LCD_STEP_CMD_DATA, 0x0010, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0011, 0x6030}, + {LCD_STEP_SLEEP, 0, 20}, + {LCD_STEP_CMD_DATA, 0x0005, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0006, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0016, 0xEF1C}, + {LCD_STEP_CMD_DATA, 0x0017, 0x0003}, + {LCD_STEP_CMD_DATA, 0x0007, 0x0233}, + {LCD_STEP_CMD_DATA, 0x000B, 0x5312}, + {LCD_STEP_CMD_DATA, 0x000F, 0x0000}, + {LCD_STEP_SLEEP, 0, 20}, + {LCD_STEP_CMD_DATA, 0x0041, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0042, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0048, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0049, 0x013F}, + {LCD_STEP_CMD_DATA, 0x0044, 0xEF00}, + {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0046, 0x013F}, + {LCD_STEP_CMD_DATA, 0x004A, 0x0000}, + {LCD_STEP_CMD_DATA, 0x004B, 0x0000}, + {LCD_STEP_SLEEP, 0, 20}, + {LCD_STEP_CMD_DATA, 0x0030, 0x0707}, + {LCD_STEP_CMD_DATA, 0x0031, 0x0704}, + {LCD_STEP_CMD_DATA, 0x0032, 0x0204}, + {LCD_STEP_CMD_DATA, 0x0033, 0x0201}, + {LCD_STEP_CMD_DATA, 0x0034, 0x0203}, + {LCD_STEP_CMD_DATA, 0x0035, 0x0204}, + {LCD_STEP_CMD_DATA, 0x0036, 0x0204}, + {LCD_STEP_CMD_DATA, 0x0037, 0x0502}, + {LCD_STEP_CMD_DATA, 0x003A, 0x0302}, + {LCD_STEP_CMD_DATA, 0x003B, 0x0500}, + {LCD_STEP_SLEEP, 0, 20}, + {LCD_STEP_CMD_DATA, 0x0044, 239 << 8 | 0}, + {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0046, 319}, + {LCD_STEP_DONE, 0, 0}, +}; + +const struct ubicom32lcd_panel tft2n0369ep = { + .desc = "TFT2N0369E-Portrait", + .init_seq = tft2n0369ep_init, + .horz_reg = 0x4e, + .vert_reg = 0x4f, + .gram_reg = 0x22, + .xres = 240, + .yres = 320, + .stride = 240, + .id = 0x8989, +}; +#endif + +#ifdef CONFIG_LCD_UBICOM32_TFT2N0369E_L +static const struct ubicom32lcd_step tft2n0369e_init[] = { + {LCD_STEP_CMD_DATA, 0x0028, 0x0006}, + {LCD_STEP_CMD_DATA, 0x0000, 0x0001}, + {LCD_STEP_SLEEP, 0, 15}, + {LCD_STEP_CMD_DATA, 0x002B, 0x9532}, + {LCD_STEP_CMD_DATA, 0x0003, 0xAAAC}, + {LCD_STEP_CMD_DATA, 0x000C, 0x0002}, + {LCD_STEP_CMD_DATA, 0x000D, 0x000A}, + {LCD_STEP_CMD_DATA, 0x000E, 0x2C00}, + {LCD_STEP_CMD_DATA, 0x001E, 0x00AA}, + {LCD_STEP_CMD_DATA, 0x0025, 0x8000}, + {LCD_STEP_SLEEP, 0, 15}, + {LCD_STEP_CMD_DATA, 0x0001, 0x2B3F}, + {LCD_STEP_CMD_DATA, 0x0002, 0x0600}, + {LCD_STEP_CMD_DATA, 0x0010, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0011, 0x60A8}, + {LCD_STEP_SLEEP, 0, 20}, + {LCD_STEP_CMD_DATA, 0x0005, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0006, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0016, 0xEF1C}, + {LCD_STEP_CMD_DATA, 0x0017, 0x0003}, + {LCD_STEP_CMD_DATA, 0x0007, 0x0233}, + {LCD_STEP_CMD_DATA, 0x000B, 0x5312}, + {LCD_STEP_CMD_DATA, 0x000F, 0x0000}, + {LCD_STEP_SLEEP, 0, 20}, + {LCD_STEP_CMD_DATA, 0x0041, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0042, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0048, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0049, 0x013F}, + {LCD_STEP_CMD_DATA, 0x0044, 0xEF00}, + {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0046, 0x013F}, + {LCD_STEP_CMD_DATA, 0x004A, 0x0000}, + {LCD_STEP_CMD_DATA, 0x004B, 0x0000}, + {LCD_STEP_SLEEP, 0, 20}, + {LCD_STEP_CMD_DATA, 0x0030, 0x0707}, + {LCD_STEP_CMD_DATA, 0x0031, 0x0704}, + {LCD_STEP_CMD_DATA, 0x0032, 0x0204}, + {LCD_STEP_CMD_DATA, 0x0033, 0x0201}, + {LCD_STEP_CMD_DATA, 0x0034, 0x0203}, + {LCD_STEP_CMD_DATA, 0x0035, 0x0204}, + {LCD_STEP_CMD_DATA, 0x0036, 0x0204}, + {LCD_STEP_CMD_DATA, 0x0037, 0x0502}, + {LCD_STEP_CMD_DATA, 0x003A, 0x0302}, + {LCD_STEP_CMD_DATA, 0x003B, 0x0500}, + {LCD_STEP_SLEEP, 0, 20}, + {LCD_STEP_CMD_DATA, 0x0044, 239 << 8 | 0}, + {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0046, 319}, + {LCD_STEP_DONE, 0, 0}, +}; + +const struct ubicom32lcd_panel tft2n0369e = { + .desc = "TFT2N0369E-Landscape", + .init_seq = tft2n0369e_init, + .horz_reg = 0x4e, + .vert_reg = 0x4f, + .gram_reg = 0x22, + .xres = 320, + .yres = 240, + .stride = 320, + .id = 0x8989, +}; +#endif + +#ifdef CONFIG_LCD_UBICOM32_CFAF240400D +static const struct ubicom32lcd_step cfaf240400d_init[] = { + {LCD_STEP_CMD_DATA, 0x0606, 0x0000}, // Pin Control (R606h) // Page 41 of SPFD5420A Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0007, 0x0001}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0110, 0x0001}, // Power Control 6(R110h) // Page 30 of SPFD5420A Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0100, 0x17B0}, // Power Control 1 (R100h) // Page 26 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0101, 0x0147}, // Power Control 2 (R101h) // Page 27 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0102, 0x019D}, // Power Control 3 (R102h) // Page 28 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0103, 0x3600}, // Power Control 4 (R103h) // Page 29 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0281, 0x0010}, // NVM read data 2 (R281h) // Page 34 of SPFD5420A Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0102, 0x01BD}, // Power Control 3 (R102h) // Page 28 of SPFD5420A Datasheet + {LCD_STEP_SLEEP, 0, 50}, + + //--------------- Power control 1~6 ---------------// + {LCD_STEP_CMD_DATA, 0x0100, 0x16B0}, // Power Control 1 (R100h) // Page 26 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0101, 0x0147}, // Power Control 2 (R101h) // Page 27 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0102, 0x01BD}, // Power Control 3 (R102h) // Page 28 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0103, 0x2d00}, // Power Control 4 (R103h) // Page 29 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0107, 0x0000}, // Power Control 5 (R107h) // Page 30 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0110, 0x0001}, // Power Control 6(R110h) // Page 30 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0280, 0x0000}, // NVM read data 1 (R280h) // Page 33 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0281, 0x0006}, // NVM read data 2 (R281h) // Page 34 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0282, 0x0000}, // NVM read data 3 (R282h) // Page 34 of SPFD5420A Datasheet + + //------- Gamma 2.2 control (R300h to R30Fh) ------// + {LCD_STEP_CMD_DATA, 0x0300, 0x0101}, + {LCD_STEP_CMD_DATA, 0x0301, 0x0b27}, + {LCD_STEP_CMD_DATA, 0x0302, 0x132a}, + {LCD_STEP_CMD_DATA, 0x0303, 0x2a13}, + {LCD_STEP_CMD_DATA, 0x0304, 0x270b}, + {LCD_STEP_CMD_DATA, 0x0305, 0x0101}, + {LCD_STEP_CMD_DATA, 0x0306, 0x1205}, + {LCD_STEP_CMD_DATA, 0x0307, 0x0512}, + {LCD_STEP_CMD_DATA, 0x0308, 0x0005}, + {LCD_STEP_CMD_DATA, 0x0309, 0x0003}, + {LCD_STEP_CMD_DATA, 0x030A, 0x0f04}, + {LCD_STEP_CMD_DATA, 0x030B, 0x0f00}, + {LCD_STEP_CMD_DATA, 0x030C, 0x000f}, + {LCD_STEP_CMD_DATA, 0x030D, 0x040f}, + {LCD_STEP_CMD_DATA, 0x030E, 0x0300}, + {LCD_STEP_CMD_DATA, 0x030F, 0x0500}, + + {LCD_STEP_CMD_DATA, 0x0400, 0x3500}, // Base Image Number of Line (R400h) // Page 36 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0401, 0x0001}, // Base Image Display Control (R401h) // Page 39 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0404, 0x0000}, // Based Image Vertical Scroll Control (R404h) // Page 40 of SPFD5420A Datasheet + + //--------------- Normal set ---------------// + {LCD_STEP_CMD_DATA, 0x0000, 0x0000}, // ID Read Register (R000h) // Page 13 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0001, 0x0100}, // Driver Output Control Register (R001h) // Page 14 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0002, 0x0100}, // LCD Driving Waveform Control (R002h) // Page 14 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0003, 0x1030}, // Entry Mode (R003h) // Page 15 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0006, 0x0000}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0008, 0x0808}, // Display Control 2 (R008h) // Page 17 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0009, 0x0001}, // Display Control 3 (R009h) // Page 18 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x000B, 0x0010}, // Low Power Control (R00Bh) // Page 19 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x000C, 0x0000}, // External Display Interface Control 1 (R00Ch) // Page 19 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x000F, 0x0000}, // External Display Interface Control 2 (R00Fh) // Page 20 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0007, 0x0001}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet + + //--------------- Panel interface control 1~6 ---------------// + {LCD_STEP_CMD_DATA, 0x0010, 0x0012}, // Panel Interface Control 1 (R010h) // Page 20 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0011, 0x0202}, // Panel Interface Control 2 (R011h) // Page 21 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0012, 0x0300}, // Panel Interface control 3 (R012h) // Page 22 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0020, 0x021E}, // Panel Interface control 4 (R020h) // Page 22 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0021, 0x0202}, // Panel Interface Control 5 (021Rh) // Page 24 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0022, 0x0100}, // Panel Interface Control 6 (R022h) // Page 25 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0090, 0x8000}, // Frame Marker Control (R090h) // Page 25 of SPFD5420A Datasheet + + //--------------- Partial display ---------------// + {LCD_STEP_CMD_DATA, 0x0210, 0x0000}, // Window Horizontal RAM Address Start (R210h) // Page 35 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0211, 0x00EF}, // Window Horziontal RAM Address End (R211h) // Page 35 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0212, 0x0000}, // Window Vertical RAM Address Start (R212h) // Page 35 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0213, 0x018F}, // Window Vertical RAM Address End (R213h) // Page 35 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0500, 0x0000}, // Display Position - Partial Display 1 (R500h) // Page 40 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0501, 0x0000}, // RAM Address Start - Partial Display 1 (R501h)// Page 40 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0502, 0x0000}, // RAM Address End - Partail Display 1 (R502h) // Page 40 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0503, 0x0000}, // Display Position - Partial Display 2 (R503h) // Page 40 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0504, 0x0000}, // RAM Address Start . Partial Display 2 (R504h)// Page 41 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0505, 0x0000}, // RAM Address End . Partial Display 2 (R505h) // Page 41 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0606, 0x0000}, // Pin Control (R606h) // Page 41 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x06F0, 0x0000}, // NVM Access Control (R6F0h) // Page 41 of SPFD5420A Datasheet + {LCD_STEP_CMD_DATA, 0x0007, 0x0173}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet + {LCD_STEP_SLEEP, 0, 50}, + {LCD_STEP_CMD_DATA, 0x0007, 0x0171}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet + {LCD_STEP_SLEEP, 0, 10}, + {LCD_STEP_CMD_DATA, 0x0007, 0x0173}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet + {LCD_STEP_DONE, 0, 0}, +}; + +const struct ubicom32lcd_panel cfaf240400d = { + .desc = "CFAF240400D", + .init_seq = cfaf240400d_init, + .horz_reg = 0x0200, + .vert_reg = 0x0201, + .gram_reg = 0x0202, + .xres = 240, + .yres = 400, + .stride = 240, + .id = 0x5420, +}; +#endif + +#ifdef CONFIG_LCD_UBICOM32_CFAF240400F +static const struct ubicom32lcd_step cfaf320240f_init[] = { + {LCD_STEP_CMD_DATA, 0x0028, 0x0006}, // VCOM OTP Page 55-56 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0000, 0x0001}, // start Oscillator Page 36 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0010, 0x0000}, // Sleep mode Page 49 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0001, 0x32EF}, // Driver Output Control Page 36-39 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0002, 0x0600}, // LCD Driving Waveform Control Page 40-42 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0003, 0x6A38}, // Power Control 1 Page 43-44 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0011, 0x6870}, // Entry Mode Page 50-52 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0X000F, 0x0000}, // Gate Scan Position Page 49 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0X000B, 0x5308}, // Frame Cycle Control Page 45 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x000C, 0x0003}, // Power Control 2 Page 47 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x000D, 0x000A}, // Power Control 3 Page 48 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x000E, 0x2E00}, // Power Control 4 Page 48 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x001E, 0x00BE}, // Power Control 5 Page 53 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0025, 0x8000}, // Frame Frequency Control Page 53 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0026, 0x7800}, // Analog setting Page 54 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x004E, 0x0000}, // Ram Address Set Page 58 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x004F, 0x0000}, // Ram Address Set Page 58 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0012, 0x08D9}, // Sleep mode Page 49 of SSD2119 datasheet + + // Gamma Control (R30h to R3Bh) -- Page 56 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0030, 0x0000}, + {LCD_STEP_CMD_DATA, 0x0031, 0x0104}, + {LCD_STEP_CMD_DATA, 0x0032, 0x0100}, + {LCD_STEP_CMD_DATA, 0x0033, 0x0305}, + {LCD_STEP_CMD_DATA, 0x0034, 0x0505}, + {LCD_STEP_CMD_DATA, 0x0035, 0x0305}, + {LCD_STEP_CMD_DATA, 0x0036, 0x0707}, + {LCD_STEP_CMD_DATA, 0x0037, 0x0300}, + {LCD_STEP_CMD_DATA, 0x003A, 0x1200}, + {LCD_STEP_CMD_DATA, 0x003B, 0x0800}, + + {LCD_STEP_CMD_DATA, 0x0007, 0x0033}, // Display Control Page 45 of SSD2119 datasheet + + {LCD_STEP_CMD_DATA, 0x0044, 0xEF00}, // Vertical RAM address position Page 57 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, // Horizontal RAM address position Page 57 of SSD2119 datasheet + {LCD_STEP_CMD_DATA, 0x0046, 0x013F}, // Horizontal RAM address position Page 57 of SSD2119 datasheet + + {LCD_STEP_SLEEP, 0, 150}, + + {LCD_STEP_DONE, 0, 0}, +}; + +const struct ubicom32lcd_panel cfaf320240f = { + .desc = "CFAF320240F", + .init_seq = cfaf320240f_init, + .horz_reg = 0x4e, + .vert_reg = 0x4f, + .gram_reg = 0x22, + .xres = 320, + .yres = 240, + .stride = 320, + .id = 0x9919, +}; +#endif + +const struct ubicom32lcd_panel *ubicom32lcd_panels[] = { +#ifdef CONFIG_LCD_UBICOM32_CFAF240400KTTS_180 + &cfaf240320ktts_180, +#endif +#ifdef CONFIG_LCD_UBICOM32_CFAF240400KTTS + &cfaf240320ktts_0, +#endif +#ifdef CONFIG_LCD_UBICOM32_CFAF240400D + &cfaf240400d, +#endif +#ifdef CONFIG_LCD_UBICOM32_TFT2N0369E_P + &tft2n0369ep, +#endif +#ifdef CONFIG_LCD_UBICOM32_TFT2N0369E_L + &tft2n0369e, +#endif +#ifdef CONFIG_LCD_UBICOM32_CFAF240400F + &cfaf320240f, +#endif + NULL, +}; + +#endif diff --git a/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcdpower.c b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcdpower.c new file mode 100644 index 0000000000..6aeed43bc8 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/video/backlight/ubicom32lcdpower.c @@ -0,0 +1,194 @@ +/* + * drivers/video/backlight/ubicom32lcdpowerpower.c + * LCD power 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "ubicom32lcdpower" + +struct ubicom32lcdpower_data { + /* + * Pointer to the platform data structure. Keep this around since we need values + * from it to set the backlight intensity. + */ + const struct ubicom32lcdpower_platform_data *pdata; + + /* + * LCD device, we have to save this for use when we remove ourselves. + */ + struct lcd_device *lcddev; +}; + +/* + * ubicom32lcdpower_set_power + */ +static int ubicom32lcdpower_set_power(struct lcd_device *ld, int power) +{ + struct ubicom32lcdpower_data *ud = (struct ubicom32lcdpower_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; +} + +/* + * ubicom32lcdpower_get_power + */ +static int ubicom32lcdpower_get_power(struct lcd_device *ld) +{ + struct ubicom32lcdpower_data *ud = (struct ubicom32lcdpower_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 ubicom32lcdpower_ops = { + .get_power = ubicom32lcdpower_get_power, + .set_power = ubicom32lcdpower_set_power, +}; + +/* + * ubicom32lcdpower_probe + */ +static int ubicom32lcdpower_probe(struct platform_device *pdev) +{ + const struct ubicom32lcdpower_platform_data *pdata = pdev->dev.platform_data; + struct ubicom32lcdpower_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 ubicom32lcdpower_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, &ubicom32lcdpower_ops); + if (IS_ERR(lcddev)) { + retval = PTR_ERR(lcddev); + goto fail; + } + + ud->lcddev = lcddev; + platform_set_drvdata(pdev, ud); + + ubicom32lcdpower_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; +} + +/* + * ubicom32lcdpower_remove + */ +static int __exit ubicom32lcdpower_remove(struct platform_device *pdev) +{ + struct ubicom32lcdpower_data *ud = platform_get_drvdata(pdev); + + lcd_device_unregister(ud->lcddev); + platform_set_drvdata(pdev, NULL); + kfree(ud); + + return 0; +} + +static struct platform_driver ubicom32lcdpower_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + + .remove = __exit_p(ubicom32lcdpower_remove), +}; + +/* + * ubicom32lcdpower_init + */ +static int __init ubicom32lcdpower_init(void) +{ + return platform_driver_probe(&ubicom32lcdpower_driver, ubicom32lcdpower_probe); +} +module_init(ubicom32lcdpower_init); + +/* + * ubicom32lcdpower_exit + */ +static void __exit ubicom32lcdpower_exit(void) +{ + platform_driver_unregister(&ubicom32lcdpower_driver); +} +module_exit(ubicom32lcdpower_exit); + +MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); +MODULE_DESCRIPTION("Ubicom32 lcd power driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/ubicom32/files/drivers/video/ubicom32fb.c b/target/linux/ubicom32/files/drivers/video/ubicom32fb.c new file mode 100644 index 0000000000..4193560f7c --- /dev/null +++ b/target/linux/ubicom32/files/drivers/video/ubicom32fb.c @@ -0,0 +1,779 @@ +/* + * 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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_UBICOM32, +}; + +/* + * 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 = vram_size * 1024; + ud->fbinfo->fix.line_length = regs->xres * (regs->bpp / 8); + ud->fbinfo->fix.mmio_start = (u32)regs; + ud->fbinfo->fix.mmio_len = sizeof(struct vdc_tio_vp_regs); + + /* + * 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:') + */ + 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); diff --git a/target/linux/ubicom32/files/drivers/video/ubicom32plio80.c b/target/linux/ubicom32/files/drivers/video/ubicom32plio80.c new file mode 100644 index 0000000000..2e13fd7075 --- /dev/null +++ b/target/linux/ubicom32/files/drivers/video/ubicom32plio80.c @@ -0,0 +1,780 @@ +/* + * drivers/video/ubicom32plio80.c + * Ubicom32 80 bus PLIO 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 . + */ + +/* + * This driver was based on skeletonfb.c, Skeleton for a frame buffer device by + * Geert Uytterhoeven. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "ubicom32plio80" +#define DRIVER_DESCRIPTION "Ubicom32 80 bus PLIO 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 = 240; +module_param(xres, int, 0); +MODULE_PARM_DESC(xres, "x (horizontal) resolution"); + +static int yres = 320; +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)"); + +#define BITS_PER_PIXEL 16 + +/* + * Buffer alignment, must not be 0 + */ +#define UBICOM32PLIO80_ALIGNMENT 4 + +/* + * PLIO FSM + * 16-bit data bus on port I + * CS on EXTCTL[6] + * WR on EXTCTL[4] + */ +static const plio_fctl_t plio_fctl = { + .fctl0 = { + .ptif_port_mode = PLIO_PORT_MODE_DI, + .ptif_portd_cfg = 0, + .ptif_porti_cfg = 3, + .edif_ds = 6, + .edif_cmp_mode = 1, + .ecif_extclk_ena = 0, // enable clock output on PD7 table 2.65/p111 says extctl[0]? + .icif_clk_src_sel = PLIO_CLK_IO, + }, + .fctl2 = { + .icif_eclk_div = 10, + .icif_iclk_div = 10, + }, + + }; + + static const plio_config_t plio_config = { + .pfsm = { + /* + * Table 12.63 + */ + .grpsel[0] = {1,1,1,1,1,1,1,1,1,1}, + + /* + * Table 12.66 Counter load value + */ + .cs_lut[0] = {0,0,0,0,0,0,0,0}, + + /* + * Table 2.75 PLIO PFSM Configuration Registers + */ + // 3 2 1 0 + .extctl_o_lut[0] = {0x3f, 0x2f, 0x3f, 0x3f}, + // 7 6 5 4 + .extctl_o_lut[1] = {0x3f, 0x3f, 0x3f, 0x2f}, + }, + .edif = { + .odr_oe = 0xffff, + }, + .ecif = { + .output_ena = (1 << 6) | (1 << 4), + }, +}; + +static const u32_t ubicom32plio80_plio_fsm[] = { + // 0-F + 0x00070007, 0x00070007, + 0x00070007, 0x00070007, + 0x00070007, 0x00070007, + 0x00070007, 0x00070007, + + 0x16260806, 0x16260806, + 0x16260806, 0x16260806, + 0x16260806, 0x16260806, + 0x16260806, 0x16260806, + + // 10 - 1f + 0x22061806, 0x22061806, + 0x22061806, 0x22061806, + 0x22061806, 0x22061806, + 0x22061806, 0x22061806, + + 0x22061806, 0x22061806, + 0x22061806, 0x22061806, + 0x22061806, 0x22061806, + 0x22061806, 0x22061806, + + // 20 - 2f + 0x00070806, 0x00070806, + 0x00070806, 0x00070806, + 0x00070806, 0x00070806, + 0x00070806, 0x00070806, + + 0x00070806, 0x00070806, + 0x00070806, 0x00070806, + 0x00070806, 0x00070806, + 0x00070806, 0x00070806, +}; + +/* + * fb_fix_screeninfo defines the non-changeable properties of the VDC, depending on what mode it is in. + */ +static struct fb_fix_screeninfo ubicom32plio80_fix = { + .id = "Ubicom32", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .accel = FB_ACCEL_UBICOM32_PLIO80, +}; + +/* + * Filled in at probe time when we find out what the hardware supports + */ +static struct fb_var_screeninfo ubicom32plio80_var; + +/* + * Private data structure + */ +struct ubicom32plio80_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]; + + int irq_req; + + /* + * Current pointer and bytes left to transfer with the PLIO + */ + void *xfer_ptr; + u32 bytes_to_xfer; + u32 busy; +}; + +static struct platform_device *ubicom32plio80_platform_device; + +/* + * ubicom32plio80_isr + */ +static int ubicom32plio80_isr(int irq, void *appdata) +{ + struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)appdata; + + if (!ud->bytes_to_xfer) { + ubicom32_disable_interrupt(TX_FIFO_INT(PLIO_PORT)); + PLIO_NBR->intmask.txfifo_wm = 0; + ud->busy = 0; + return IRQ_HANDLED; + } + + asm volatile ( + ".rept 8 \n\t" + "move.4 (%[fifo]), (%[data])4++ \n\t" + ".endr \n\t" + : [data] "+a" (ud->xfer_ptr) + : [fifo] "a" (&PLIO_NBR->tx_lo) + ); + + ud->bytes_to_xfer -= 32; + + return IRQ_HANDLED; +} + +/* + * ubicom32plio80_update + */ +static void ubicom32plio80_update(struct ubicom32plio80_drvdata *ud, u32 *fb) +{ + struct ubicom32_io_port *ri = (struct ubicom32_io_port *)RI; + struct ubicom32_io_port *rd = (struct ubicom32_io_port *)RD; + + ud->xfer_ptr = fb; + ud->bytes_to_xfer = (xres * yres * 2) - 64; + ud->busy = 1; + + ri->gpio_mask = 0; + rd->gpio_mask &= ~((1 << 4) | (1 << 2)); + + *(u32 *)(&PLIO_NBR->intclr) = ~0; + PLIO_NBR->intmask.txfifo_wm = 1; + PLIO_NBR->fifo_wm.tx = 8; + ubicom32_enable_interrupt(TX_FIFO_INT(PLIO_PORT)); + + asm volatile ( + ".rept 16 \n\t" + "move.4 (%[fifo]), (%[data])4++ \n\t" + ".endr \n\t" + : [data] "+a" (ud->xfer_ptr) + : [fifo] "a" (&PLIO_NBR->tx_lo) + ); +} + +/* + * ubicom32plio80_pan_display + * Pans the display to a given location. Supports only y direction panning. + */ +static int ubicom32plio80_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) +{ + struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_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; +} + +/* + * ubicom32plio80_setcolreg + * Sets a color in our virtual palette + */ +static int ubicom32plio80_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; +} + +/* + * ubicom32plio80_mmap + */ +static int ubicom32plio80_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)info->par; + + vma->vm_start = (unsigned long)(ud->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; +} + +/* + * ubicom32plio80_check_var + * Check the var, tweak it but don't change operational parameters. + */ +static int ubicom32plio80_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)info->par; + u32 line_size = var->xres * (BITS_PER_PIXEL / 8); + + /* + * See if we can handle this bpp + */ + if (var->bits_per_pixel > BITS_PER_PIXEL) { + return -EINVAL; + } + var->bits_per_pixel = BITS_PER_PIXEL; + + /* + * See if we have enough memory to handle this resolution + */ + if ((line_size * var->yres * BITS_PER_PIXEL / 8) > ud->total_vram_size) { + return -EINVAL; + } + + var->xres_virtual = var->xres; + var->yres_virtual = ud->total_vram_size / line_size; + + var->red.length = 5; + var->green.length = 6; + var->green.offset = 5; + var->blue.length = 5; + var->transp.offset = var->transp.length = 0; + + if (bgr) { + var->red.offset = 0; + var->blue.offset = 11; + } else { + var->red.offset = 11; + var->blue.offset = 0; + } + + var->nonstd = 0; + var->height = -1; + var->width = -1; + var->vmode = FB_VMODE_NONINTERLACED; + var->sync = 0; + + return 0; +} + +/* + * ubicom32plio80_set_par + * Set the video mode according to info->var + */ +static int ubicom32plio80_set_par(struct fb_info *info) +{ + /* + * Anything changed? + */ + if ((xres == info->var.xres) && (yres == info->var.yres)) { + return 0; + } + + /* + * Implement changes + */ + xres = info->var.xres; + yres = info->var.yres; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.xpanstep = 0; + info->fix.ypanstep = 1; + info->fix.line_length = xres * (BITS_PER_PIXEL / 8); + + return 0; +} + +/* + * ubicom32plio80_ops + * List of supported operations + */ +static struct fb_ops ubicom32plio80_ops = +{ + .owner = THIS_MODULE, + .fb_pan_display = ubicom32plio80_pan_display, + .fb_setcolreg = ubicom32plio80_setcolreg, + .fb_mmap = ubicom32plio80_mmap, + .fb_check_var = ubicom32plio80_check_var, + .fb_set_par = ubicom32plio80_set_par, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/* + * ubicom32plio80_release + */ +static int ubicom32plio80_release(struct device *dev) +{ + struct ubicom32plio80_drvdata *ud = dev_get_drvdata(dev); + + unregister_framebuffer(ud->fbinfo); + + if (ud->irq_req) { + free_irq(TX_FIFO_INT(PLIO_PORT), ud); + } + 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; +} + +/* + * ubicom32plio80_platform_probe + */ +static int __init ubicom32plio80_platform_probe(struct platform_device *pdev) +{ + struct ubicom32plio80_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 ubicom32plio80_drvdata), &pdev->dev); + if (!fbinfo) { + dev_err(dev, "Not enough memory to allocate instance.\n"); + return -ENOMEM; + } + + /* + * Fill in our private data. + */ + ud = (struct ubicom32plio80_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) + UBICOM32PLIO80_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 & (UBICOM32PLIO80_ALIGNMENT - 1); + if (!offset) { + ud->fb_aligned = ud->fb; + } else { + offset = UBICOM32PLIO80_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(&ubicom32plio80_var, 0, sizeof(ubicom32plio80_var)); + ubicom32plio80_var.bits_per_pixel = BITS_PER_PIXEL; + ubicom32plio80_var.red.length = 5; + ubicom32plio80_var.green.length = 6; + ubicom32plio80_var.green.offset = 5; + ubicom32plio80_var.blue.length = 5; + ubicom32plio80_var.activate = FB_ACTIVATE_NOW; + + if (bgr) { + ubicom32plio80_var.red.offset = 0; + ubicom32plio80_var.blue.offset = 11; + } else { + ubicom32plio80_var.red.offset = 11; + ubicom32plio80_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 = &ubicom32plio80_ops; + ud->fbinfo->fix = ubicom32plio80_fix; + ud->fbinfo->fix.smem_start = (u32)ud->fb_aligned; + ud->fbinfo->fix.smem_len = vram_size * 1024; + ud->fbinfo->fix.line_length = xres * 2; + ud->fbinfo->fix.mmio_start = (u32)ud; + ud->fbinfo->fix.mmio_len = sizeof(struct ubicom32plio80_drvdata); + + /* + * 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 = ubicom32plio80_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; + } + + /* + * request the PLIO IRQ + */ + rc = request_irq(TX_FIFO_INT(PLIO_PORT), ubicom32plio80_isr, IRQF_DISABLED, "ubicom32plio80", ud); + if (rc) { + dev_err(dev, "Could not request IRQ\n"); + goto fail; + } + ud->irq_req = 1; + + /* + * Clear any garbage out of the TX FIFOs (idif_txfifo_flush) + * + * cast through ubicom32_io_port to make sure the compiler does a word write + */ + ((struct ubicom32_io_port *)PLIO_NBR)->int_set = (1 << 18); + + /* + * Start up the state machine + */ + plio_init(&plio_fctl, &plio_config, (plio_sram_t *)ubicom32plio80_plio_fsm, sizeof(ubicom32plio80_plio_fsm)); + PLIO_NBR->fctl0.pfsm_cmd = 0; + + ubicom32plio80_update(ud, ud->fb_aligned); + + /* + * 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: + ubicom32plio80_release(dev); + return rc; +} + +/* + * ubicom32plio80_platform_remove + */ +static int ubicom32plio80_platform_remove(struct platform_device *pdev) +{ + dev_info(&(pdev->dev), "Ubicom32 FB Driver Remove\n"); + return ubicom32plio80_release(&pdev->dev); +} + +static struct platform_driver ubicom32plio80_platform_driver = { + .probe = ubicom32plio80_platform_probe, + .remove = ubicom32plio80_platform_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +#ifndef MODULE +/* + * ubicom32plio80_setup + * Process kernel boot options + */ +static int __init ubicom32plio80_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 */ + +/* + * ubicom32plio80_init + */ +static int __devinit ubicom32plio80_init(void) +{ + int ret; + +#ifndef MODULE + /* + * Get kernel boot options (in 'video=ubicom32plio80:') + */ + char *option = NULL; + + if (fb_get_options(DRIVER_NAME, &option)) { + return -ENODEV; + } + ubicom32plio80_setup(option); +#endif /* MODULE */ + + ret = platform_driver_register(&ubicom32plio80_platform_driver); + + if (!ret) { + ubicom32plio80_platform_device = platform_device_alloc(DRIVER_NAME, 0); + + if (ubicom32plio80_platform_device) + ret = platform_device_add(ubicom32plio80_platform_device); + else + ret = -ENOMEM; + + if (ret) { + platform_device_put(ubicom32plio80_platform_device); + platform_driver_unregister(&ubicom32plio80_platform_driver); + } + } + + return ret; +} +module_init(ubicom32plio80_init); + +/* + * ubicom32plio80_exit + */ +static void __exit ubicom32plio80_exit(void) +{ + platform_device_unregister(ubicom32plio80_platform_device); + platform_driver_unregister(&ubicom32plio80_platform_driver); +} +module_exit(ubicom32plio80_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); diff --git a/target/linux/ubicom32/files/drivers/video/ubicom32vfb.c b/target/linux/ubicom32/files/drivers/video/ubicom32vfb.c new file mode 100644 index 0000000000..8478273d4c --- /dev/null +++ b/target/linux/ubicom32/files/drivers/video/ubicom32vfb.c @@ -0,0 +1,603 @@ +/* + * 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 . + */ + +/* + * This driver was based on skeletonfb.c, Skeleton for a frame buffer device by + * Geert Uytterhoeven. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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)"); + +#define BITS_PER_PIXEL 16 + +/* + * 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_UBICOM32_VFB, +}; + +/* + * 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]; +}; + +static struct platform_device *ubicom32vfb_platform_device; + +/* + * 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 *ud = (struct ubicom32vfb_drvdata *)info->par; + + vma->vm_start = (unsigned long)(ud->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; +} + +/* + * ubicom32vfb_check_var + * Check the var, tweak it but don't change operational parameters. + */ +static int ubicom32vfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct ubicom32vfb_drvdata *ud = (struct ubicom32vfb_drvdata *)info->par; + u32 line_size = var->xres * (BITS_PER_PIXEL / 8); + + /* + * See if we can handle this bpp + */ + if (var->bits_per_pixel > BITS_PER_PIXEL) { + return -EINVAL; + } + var->bits_per_pixel = BITS_PER_PIXEL; + + /* + * See if we have enough memory to handle this resolution + */ + if ((line_size * var->yres * BITS_PER_PIXEL / 8) > ud->total_vram_size) { + return -EINVAL; + } + + var->xres_virtual = var->xres; + var->yres_virtual = ud->total_vram_size / line_size; + + var->red.length = 5; + var->green.length = 6; + var->green.offset = 5; + var->blue.length = 5; + var->transp.offset = var->transp.length = 0; + + if (bgr) { + var->red.offset = 0; + var->blue.offset = 11; + } else { + var->red.offset = 11; + var->blue.offset = 0; + } + + var->nonstd = 0; + var->height = -1; + var->width = -1; + var->vmode = FB_VMODE_NONINTERLACED; + var->sync = 0; + + return 0; +} + +/* + * ubicom32vfb_set_par + * Set the video mode according to info->var + */ +static int ubicom32vfb_set_par(struct fb_info *info) +{ + /* + * Anything changed? + */ + if ((xres == info->var.xres) && (yres == info->var.yres)) { + return 0; + } + + /* + * Implement changes + */ + xres = info->var.xres; + yres = info->var.yres; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.xpanstep = 0; + info->fix.ypanstep = 1; + info->fix.line_length = xres * (BITS_PER_PIXEL / 8); + + return 0; +} + +/* + * ubicom32vfb_ops + * List of supported operations + */ +static struct fb_ops ubicom32vfb_ops = +{ + .owner = THIS_MODULE, + .fb_pan_display = ubicom32vfb_pan_display, + .fb_setcolreg = ubicom32vfb_setcolreg, + .fb_mmap = ubicom32vfb_mmap, + .fb_check_var = ubicom32vfb_check_var, + .fb_set_par = ubicom32vfb_set_par, + .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 = BITS_PER_PIXEL; + 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 = vram_size * 1024; + ud->fbinfo->fix.line_length = xres * 2; + ud->fbinfo->fix.mmio_start = (u32)ud; + ud->fbinfo->fix.mmio_len = sizeof(struct ubicom32vfb_drvdata); + + /* + * 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) +{ + int ret; + +#ifndef MODULE + /* + * Get kernel boot options (in 'video=ubicom32vfb:') + */ + char *option = NULL; + + if (fb_get_options(DRIVER_NAME, &option)) { + return -ENODEV; + } + ubicom32vfb_setup(option); +#endif /* MODULE */ + + ret = platform_driver_register(&ubicom32vfb_platform_driver); + +#ifdef CONFIG_FB_UBICOM32_VIRTUAL_NOAUTO + return ret; +#else + if (!ret) { + ubicom32vfb_platform_device = platform_device_alloc(DRIVER_NAME, 0); + + if (ubicom32vfb_platform_device) + ret = platform_device_add(ubicom32vfb_platform_device); + else + ret = -ENOMEM; + + if (ret) { + platform_device_put(ubicom32vfb_platform_device); + platform_driver_unregister(&ubicom32vfb_platform_driver); + } + } + + return ret; +#endif +} +module_init(ubicom32vfb_init); + +/* + * ubicom32vfb_exit + */ +static void __exit ubicom32vfb_exit(void) +{ + platform_device_unregister(ubicom32vfb_platform_device); + platform_driver_unregister(&ubicom32vfb_platform_driver); +} +module_exit(ubicom32vfb_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); diff --git a/target/linux/ubicom32/files/drivers/watchdog/ubi32_wdt.c b/target/linux/ubicom32/files/drivers/watchdog/ubi32_wdt.c new file mode 100644 index 0000000000..2c5b92187c --- /dev/null +++ b/target/linux/ubicom32/files/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 + * (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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); +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) ")"); diff --git a/target/linux/ubicom32/files/sound/ubicom32/Kconfig b/target/linux/ubicom32/files/sound/ubicom32/Kconfig new file mode 100644 index 0000000000..c57bd34d97 --- /dev/null +++ b/target/linux/ubicom32/files/sound/ubicom32/Kconfig @@ -0,0 +1,42 @@ +# 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_CAPTURE + bool "Generic Capture Support" + default n + help + Use this option to support ADCs which don't require special drivers. + +config SND_UBI32_AUDIO_GENERIC + bool "Generic Playback Support" + default n + help + Use this option to support DACs which don't require special drivers. + +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 diff --git a/target/linux/ubicom32/files/sound/ubicom32/Makefile b/target/linux/ubicom32/files/sound/ubicom32/Makefile new file mode 100644 index 0000000000..ffdcc298a1 --- /dev/null +++ b/target/linux/ubicom32/files/sound/ubicom32/Makefile @@ -0,0 +1,41 @@ +# +# 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 . +# +# 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-generic-capture-objs := ubi32-generic-capture.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_GENERIC_CAPTURE) += snd-ubi32-generic-capture.o +obj-$(CONFIG_SND_UBI32_AUDIO_CS4350) += snd-ubi32-cs4350.o +obj-$(CONFIG_SND_UBI32_AUDIO_CS4384) += snd-ubi32-cs4384.o diff --git a/target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4350.c b/target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4350.c new file mode 100644 index 0000000000..7e6f9acbb3 --- /dev/null +++ b/target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4350.c @@ -0,0 +1,583 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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 */ + +/* + * The dB scale for the Cirrus Logic cs4350. The output range is from + * -127.5 dB to 0 dB. + */ +static const DECLARE_TLV_DB_SCALE(snd_ubi32_cs4350_db, -12750, 50, 0); + +#define ubi32_cs4350_mute_info snd_ctl_boolean_stereo_info + +/* + * Private data for cs4350 chip + */ +struct ubi32_cs4350_priv { + /* + * The current volume settings + */ + uint8_t volume[2]; + + /* + * Bitmask of mutes MSB (unused, ..., unused, right_ch, left_ch) LSB + */ + uint8_t mute; + + /* + * Lock to protect this struct because callbacks are not atomic. + */ + spinlock_t lock; +}; + +/* + * The info for the cs4350. The volume currently has one channel, + * and 255 possible settings. + */ +static int ubi32_cs4350_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; // 8 bits in cirrus logic cs4350 volume register + return 0; +} + +static int ubi32_cs4350_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol); + struct ubi32_cs4350_priv *cs4350_priv; + unsigned long flags; + + cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); + + spin_lock_irqsave(&cs4350_priv->lock, flags); + + ucontrol->value.integer.value[0] = cs4350_priv->volume[0]; + ucontrol->value.integer.value[1] = cs4350_priv->volume[1]; + + spin_unlock_irqrestore(&cs4350_priv->lock, flags); + + return 0; +} + +static int ubi32_cs4350_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol); + struct i2c_client *client = (struct i2c_client *)ubi32_priv->client; + struct ubi32_cs4350_priv *cs4350_priv; + unsigned long flags; + int ret, changed; + char send[2]; + uint8_t volume_reg_value_left, volume_reg_value_right; + + changed = 0; + + cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); + volume_reg_value_left = 255 - (ucontrol->value.integer.value[0] & 0xFF); + volume_reg_value_right = 255 - (ucontrol->value.integer.value[1] & 0xFF); + +#if SND_UBI32_DEBUG + snd_printk(KERN_INFO "Setting volume: writing %d,%d to CS4350 volume registers\n", volume_reg_value_left, volume_reg_value_right); +#endif + spin_lock_irqsave(&cs4350_priv->lock, flags); + + if (cs4350_priv->volume[0] != ucontrol->value.integer.value[0]) { + send[0] = 0x05; // left channel + send[1] = volume_reg_value_left; + ret = i2c_master_send(client, send, 2); + if (ret != 2) { + snd_printk(KERN_ERR "Failed to set channel A volume on CS4350\n"); + return changed; + } + cs4350_priv->volume[0] = ucontrol->value.integer.value[0]; + changed = 1; + } + + if (cs4350_priv->volume[1] != ucontrol->value.integer.value[1]) { + send[0] = 0x06; // right channel + send[1] = volume_reg_value_right; + ret = i2c_master_send(client, send, 2); + if (ret != 2) { + snd_printk(KERN_ERR "Failed to set channel B volume on CS4350\n"); + return changed; + } + cs4350_priv->volume[1] = ucontrol->value.integer.value[1]; + changed = 1; + } + + spin_unlock_irqrestore(&cs4350_priv->lock, flags); + + return changed; +} + +static struct snd_kcontrol_new ubi32_cs4350_volume __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "PCM Playback Volume", + .info = ubi32_cs4350_volume_info, + .get = ubi32_cs4350_volume_get, + .put = ubi32_cs4350_volume_put, + .tlv.p = snd_ubi32_cs4350_db, +}; + +static int ubi32_cs4350_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol); + struct ubi32_cs4350_priv *cs4350_priv; + unsigned long flags; + + cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); + + spin_lock_irqsave(&cs4350_priv->lock, flags); + + ucontrol->value.integer.value[0] = cs4350_priv->mute & 1; + ucontrol->value.integer.value[1] = (cs4350_priv->mute & (1 << 1)) ? 1 : 0; + + spin_unlock_irqrestore(&cs4350_priv->lock, flags); + + return 0; +} + +static int ubi32_cs4350_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol); + struct i2c_client *client = (struct i2c_client *)ubi32_priv->client; + struct ubi32_cs4350_priv *cs4350_priv; + unsigned long flags; + int ret, changed; + char send[2]; + char recv[1]; + uint8_t mute; + + changed = 0; + + cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); + + spin_lock_irqsave(&cs4350_priv->lock, flags); + + if ((cs4350_priv->mute & 1) != ucontrol->value.integer.value[0]) { + send[0] = 0x04; + ret = i2c_master_send(client, send, 1); + if (ret != 1) { + snd_printk(KERN_ERR "Failed to write to mute register: channel 0\n"); + return changed; + } + + ret = i2c_master_recv(client, recv, 1); + if (ret != 1) { + snd_printk(KERN_ERR "Failed to read mute register: channel 0\n"); + return changed; + } + + mute = recv[0]; + + if (ucontrol->value.integer.value[0]) { + cs4350_priv->mute |= 1; + mute &= ~(1 << 4); +#if SND_UBI32_DEBUG + snd_printk(KERN_INFO "Unmuted channel A\n"); +#endif + } else { + cs4350_priv->mute &= ~1; + mute |= (1 << 4); +#if SND_UBI32_DEBUG + snd_printk(KERN_INFO "Muted channel A\n"); +#endif + } + + send[0] = 0x04; + send[1] = mute; + ret = i2c_master_send(client, send, 2); + if (ret != 2) { + snd_printk(KERN_ERR "Failed to set channel A mute on CS4350\n"); + return changed; + } + changed = 1; + } + + if (((cs4350_priv->mute & 2) >> 1) != ucontrol->value.integer.value[1]) { + send[0] = 0x04; + ret = i2c_master_send(client, send, 1); + if (ret != 1) { + snd_printk(KERN_ERR "Failed to write to mute register: channel 1\n"); + return changed; + } + + ret = i2c_master_recv(client, recv, 1); + if (ret != 1) { + snd_printk(KERN_ERR "Failed to read mute register: channel 1\n"); + return changed; + } + + mute = recv[0]; + + if (ucontrol->value.integer.value[1]) { + cs4350_priv->mute |= (1 << 1); + mute &= ~(1 << 3); +#if SND_UBI32_DEBUG + snd_printk(KERN_INFO "Unmuted channel B\n"); +#endif + } else { + cs4350_priv->mute &= ~(1 << 1); + mute |= (1 << 3); +#if SND_UBI32_DEBUG + snd_printk(KERN_INFO "Muted channel B\n"); +#endif + } + + send[0] = 0x04; + send[1] = mute; + ret = i2c_master_send(client, send, 2); + if (ret != 2) { + snd_printk(KERN_ERR "Failed to set channel A mute on CS4350\n"); + return changed; + } + changed = 1; + } + + spin_unlock_irqrestore(&cs4350_priv->lock, flags); + + return changed; +} + +static struct snd_kcontrol_new ubi32_cs4350_mute __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "PCM Playback Switch", + .info = ubi32_cs4350_mute_info, + .get = ubi32_cs4350_mute_get, + .put = ubi32_cs4350_mute_put, +}; + +/* + * snd_ubi32_cs4350_free + * Card private data free function + */ +void snd_ubi32_cs4350_free(struct snd_card *card) +{ + struct ubi32_snd_priv *ubi32_priv; + struct ubi32_cs4350_priv *cs4350_priv; + + ubi32_priv = card->private_data; + cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); + if (cs4350_priv) { + kfree(cs4350_priv); + } +} + +/* + * 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 0 dB + * Channel B volume set to 0 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; + struct ubi32_cs4350_priv *cs4350_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; + ubi32_priv->client = client; + + /* + * Create our private data structure + */ + cs4350_priv = kzalloc(sizeof(struct ubi32_cs4350_priv), GFP_KERNEL); + if (!cs4350_priv) { + snd_card_free(card); + return -ENOMEM; + } + snd_ubi32_priv_set_drv(ubi32_priv, cs4350_priv); + spin_lock_init(&cs4350_priv->lock); + + /* + * Initial volume is set to max by probe function + */ + cs4350_priv->volume[0] = 0xFF; + cs4350_priv->volume[1] = 0xFF; + + /* + * The CS4350 starts off unmuted (bit set = not muted) + */ + cs4350_priv->mute = 3; + + /* + * 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"); + snprintf(card->longname, sizeof(card->longname), + "%s at sendirq=%d.%d recvirq=%d.%d regs=%p", + card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx, + ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr); + + snd_card_set_dev(card, &client->dev); + + /* + * Set up the mixer components + */ + err = snd_ctl_add(card, snd_ctl_new1(&ubi32_cs4350_volume, ubi32_priv)); + if (err) { + snd_printk(KERN_WARNING "Failed to add volume mixer control\n"); + } + err = snd_ctl_add(card, snd_ctl_new1(&ubi32_cs4350_mute, ubi32_priv)); + if (err) { + snd_printk(KERN_WARNING "Failed to add mute mixer control\n"); + } + + /* + * Register the sound card + */ + if ((err = snd_card_register(card)) != 0) { + snd_printk(KERN_WARNING "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"); diff --git a/target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4384.c b/target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4384.c new file mode 100644 index 0000000000..2679267732 --- /dev/null +++ b/target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4384.c @@ -0,0 +1,996 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 *iod = (struct ubicom32_io_port *)RD; + struct ubicom32_io_port *ioe = (struct ubicom32_io_port *)RE; + 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; + if (!ioa->function) { + ioa->function = 3; + } + return 0; + + case UBI32_CS4384_MCLK_PWM_1: + ioc->function |= 2; + ioc->ctl0 |= ctl0 << 16; + ioc->ctl2 = ctlx; + if (!ioe->function) { + ioe->function = 3; + } + return 0; + + case UBI32_CS4384_MCLK_PWM_2: + ioh->ctl0 |= ctl0; + ioh->ctl1 = ctlx; + if (!iod->function) { + iod->function = 3; + } + 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 device + */ + pdev = client->dev.platform_data; + if (!pdev) { + return -ENODEV; + } + + /* + * pdev->dev.platform_data is ubi32-pcm platform_data + */ + pdata = audio_device_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"); + snprintf(card->longname, sizeof(card->longname), + "%s at sendirq=%d.%d recvirq=%d.%d regs=%p", + card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx, + ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr); + + 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"); diff --git a/target/linux/ubicom32/files/sound/ubicom32/ubi32-generic-capture.c b/target/linux/ubicom32/files/sound/ubicom32/ubi32-generic-capture.c new file mode 100644 index 0000000000..a911cc6a1e --- /dev/null +++ b/target/linux/ubicom32/files/sound/ubicom32/ubi32-generic-capture.c @@ -0,0 +1,167 @@ +/* + * sound/ubicom32/ubi32-generic-capture.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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include "ubi32.h" + +#define DRIVER_NAME "snd-ubi32-generic-capture" + +/* + * Module properties + */ +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ + +/* + * Card private data free function + */ +void snd_ubi32_generic_capture_free(struct snd_card *card) +{ + /* + * 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_capture_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-C", THIS_MODULE, sizeof(struct ubi32_snd_priv)); + + if (card == NULL) { + return -ENOMEM; + } + + card->private_free = snd_ubi32_generic_capture_free; /* Not sure if correct */ + ubi32_priv = card->private_data; + + /* + * Initialize the snd_card's private data structure + */ + ubi32_priv->card = card; + ubi32_priv->is_capture = 1; + + /* + * 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-C"); + strcpy(card->shortname, "Ubi32-Generic-C"); + snprintf(card->longname, sizeof(card->longname), + "%s at sendirq=%d.%d recvirq=%d.%d regs=%p", + card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx, + ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr); + + 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_capture_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_capture_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = snd_ubi32_generic_capture_probe, + .remove = __devexit_p(snd_ubi32_generic_capture_remove), +}; + +/* + * snd_ubi32_generic_capture_init + */ +static int __init snd_ubi32_generic_capture_init(void) +{ + return platform_driver_register(&snd_ubi32_generic_capture_driver); +} +module_init(snd_ubi32_generic_capture_init); + +/* + * snd_ubi32_generic_capture_exit + */ +static void __exit snd_ubi32_generic_capture_exit(void) +{ + platform_driver_unregister(&snd_ubi32_generic_capture_driver); +} +module_exit(snd_ubi32_generic_capture_exit); + +/* + * Module properties + */ +//#if defined(CONFIG_SND_UBI32_AUDIO_I2C) +//MODULE_ALIAS("i2c:snd-ubi32"); +//#endif +MODULE_AUTHOR("Patrick Tjin"); +MODULE_DESCRIPTION("Driver for Ubicom32 audio devices"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/ubicom32/files/sound/ubicom32/ubi32-generic.c b/target/linux/ubicom32/files/sound/ubicom32/ubi32-generic.c new file mode 100644 index 0000000000..eee6066ceb --- /dev/null +++ b/target/linux/ubicom32/files/sound/ubicom32/ubi32-generic.c @@ -0,0 +1,166 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#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) +{ + /* + * 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"); + snprintf(card->longname, sizeof(card->longname), + "%s at sendirq=%d.%d recvirq=%d.%d regs=%p", + card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx, + ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr); + + 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"); diff --git a/target/linux/ubicom32/files/sound/ubicom32/ubi32-pcm.c b/target/linux/ubicom32/files/sound/ubicom32/ubi32-pcm.c new file mode 100644 index 0000000000..2bc300b855 --- /dev/null +++ b/target/linux/ubicom32/files/sound/ubicom32/ubi32-pcm.c @@ -0,0 +1,711 @@ +/* + * 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 . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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; + ubi32_priv->ar->int_req |= (1 << ubi32_priv->irq_idx); + ubicom32_set_interrupt(ubi32_priv->tx_irq); +} + +static snd_pcm_uframes_t snd_ubi32_pcm_pointer(struct snd_pcm_substream *substream) +{ + + struct ubi32_snd_priv *ubi32_priv = snd_pcm_substream_chip(substream); + struct audio_dev_regs *adr = ubi32_priv->adr; + 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 (!adr->primary_os_buffer_ptr) { + /* + * If primary_os_buffer_ptr 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)adr->primary_os_buffer_ptr; + 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_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data; + struct audio_dev_regs *adr = ubi32_priv->adr; + struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; + int ret = 0; + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "snd_ubi32_pcm_trigger cmd=%d=", cmd); +#endif + + if (adr->command != 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 + */ + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "START\n"); +#endif + /* + * Ready the DMA transfer + */ + ubi32_rd->period_ptr = ubi32_rd->dma_buffer; + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "trigger period_ptr=%lx\n", (unsigned long)ubi32_rd->period_ptr); +#endif + adr->dma_xfer_requests[0].ptr = (void *)ubi32_rd->period_ptr; + adr->dma_xfer_requests[0].ctr = ubi32_rd->period_size; + adr->dma_xfer_requests[0].active = 1; + +#ifdef CONFIG_SND_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; + adr->dma_xfer_requests[1].ptr = (void *)ubi32_rd->period_ptr; + adr->dma_xfer_requests[1].ctr = ubi32_rd->period_size; + adr->dma_xfer_requests[1].active = 1; + +#ifdef CONFIG_SND_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 + */ + adr->int_flags |= AUDIO_INT_FLAG_COMMAND; + adr->command = AUDIO_CMD_START; + snd_ubi32_vp_int_set(substream->pcm); + break; + + case SNDRV_PCM_TRIGGER_STOP: + +#ifdef CONFIG_SND_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 + */ + adr->int_flags |= AUDIO_INT_FLAG_COMMAND; + adr->command = AUDIO_CMD_STOP; + snd_ubi32_vp_int_set(substream->pcm); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + +#ifdef CONFIG_SND_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 + */ + adr->int_flags |= AUDIO_INT_FLAG_COMMAND; + adr->command = AUDIO_CMD_PAUSE; + snd_ubi32_vp_int_set(substream->pcm); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + +#ifdef CONFIG_SND_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 + */ + adr->int_flags |= AUDIO_INT_FLAG_COMMAND; + adr->command = 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_pcm_prepare(struct snd_pcm_substream *substream) +{ + /* + * Configure registers and setup the runtime instance for DMA transfers + */ + struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data; + struct audio_dev_regs *adr = ubi32_priv->adr; + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "snd_ubi32_pcm_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 + */ + adr->int_flags |= AUDIO_INT_FLAG_COMMAND; + adr->command = 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_pcm_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 audio_dev_regs *adr = ubi32_priv->adr; + 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 (ret < 0) { + return ret; + } + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "snd_ubi32_pcm_hw_params\n"); +#endif + + if (!(adr->channel_mask & (1 << params_channels(hw_params)))) { + snd_printk(KERN_INFO "snd_ubi32_pcm_hw_params unsupported number of channels %d mask %08x\n", params_channels(hw_params), adr->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 (adr->command != AUDIO_CMD_NONE) { + snd_printk(KERN_WARNING "snd_ubi32_pcm_hw_params: tio busy\n"); + return -EAGAIN; + } + + if (params_format(hw_params) == SNDRV_PCM_FORMAT_S16_LE) { + adr->flags |= CMD_START_FLAG_LE; + } else { + adr->flags &= ~CMD_START_FLAG_LE; + } + adr->channels = params_channels(hw_params); + adr->sample_rate = params_rate(hw_params); + adr->command = AUDIO_CMD_SETUP; + adr->int_flags |= AUDIO_INT_FLAG_COMMAND; + snd_ubi32_vp_int_set(substream->pcm); + + /* + * Wait for the command to complete + */ + while (adr->command != AUDIO_CMD_NONE) { + udelay(1); + } + + /* + * 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 + */ + adr->dma_xfer_requests[0].active = 0; + adr->dma_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); + +#ifdef CONFIG_SND_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 ret=%d\n", ubi32_rd->dma_buffer, ubi32_rd->dma_buffer_end, ret); +#endif + + return ret; +} + +/* + * This is the reverse of snd_ubi32_pcm_hw_params + */ +static int snd_ubi32_pcm_hw_free(struct snd_pcm_substream *substream) +{ +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "snd_ubi32_pcm_hw_free\n"); +#endif + return snd_pcm_lib_free_pages(substream); +} + +/* + * Audio virtual peripheral capabilities (capture and playback are identical) + */ +static struct snd_pcm_hardware snd_ubi32_pcm_hw = +{ + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .buffer_bytes_max = (64*1024), + .period_bytes_min = 64, + .period_bytes_max = 8184,//8184,//8176, + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, // THIS IS IGNORED BY ALSA +}; + +/* + * We fill this in later + */ +static struct snd_pcm_hw_constraint_list ubi32_pcm_rates; + +/* + * snd_ubi32_pcm_close + */ +static int snd_ubi32_pcm_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; + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "snd_ubi32_pcm_close\n"); +#endif + + substream->runtime->private_data = NULL; + + kfree(ubi32_rd); + + return 0; +} + +/* + * snd_ubi32_pcm_open + */ +static int snd_ubi32_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct ubi32_snd_runtime_data *ubi32_rd; + int ret = 0; + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "ubi32 pcm open\n"); +#endif + + /* Associate capabilities with component */ + runtime->hw = snd_ubi32_pcm_hw; + + /* + * Inform ALSA about constraints of the audio device + */ + ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &ubi32_pcm_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; + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "snd_ubi32_pcm_open returned 0\n"); +#endif + + return 0; +out: +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "snd_ubi32_pcm_open returned %d\n", ret); +#endif + + return ret; +} + +static struct snd_pcm_ops snd_ubi32_pcm_ops = { + .open = snd_ubi32_pcm_open, /* Open */ + .close = snd_ubi32_pcm_close, /* Close */ + .ioctl = snd_pcm_lib_ioctl, /* Generic IOCTL handler */ + .hw_params = snd_ubi32_pcm_hw_params, /* Hardware parameters/capabilities */ + .hw_free = snd_ubi32_pcm_hw_free, /* Free function for hw_params */ + .prepare = snd_ubi32_pcm_prepare, + .trigger = snd_ubi32_pcm_trigger, + .pointer = snd_ubi32_pcm_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 audio_dev_regs *adr = ubi32_priv->adr; + struct snd_pcm_substream *substream; + struct ubi32_snd_runtime_data *ubi32_rd; + int dma_to_fill = 0; + + /* + * Check to see if the interrupt is for us + */ + if (!(ubi32_priv->ar->int_status & (1 << ubi32_priv->irq_idx))) { + return IRQ_NONE; + } + + /* + * Clear the interrupt + */ + ubi32_priv->ar->int_status &= ~(1 << ubi32_priv->irq_idx); + + /* + * We only have one stream since we don't mix. Therefore + * we don't need to search through substreams. + */ + if (ubi32_priv->is_capture) { + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + } else { + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + } + + if (!substream->runtime) { + snd_printk(KERN_WARNING "No runtime data\n"); + return IRQ_NONE; + } + + ubi32_rd = substream->runtime->private_data; + +#ifdef CONFIG_SND_DEBUG + snd_printk(KERN_INFO "Ubi32 ALSA interrupt\n"); +#endif + + if (ubi32_rd == NULL) { + snd_printk(KERN_WARNING "No private data\n"); + return IRQ_NONE; + } + + // Check interrupt cause + if (0) { + // Handle the underflow case + } else if ((adr->status & AUDIO_STATUS_PLAY_DMA0_REQUEST) || + (adr->status & AUDIO_STATUS_PLAY_DMA1_REQUEST)) { + if (adr->status & AUDIO_STATUS_PLAY_DMA0_REQUEST) { + dma_to_fill = 0; + adr->status &= ~AUDIO_STATUS_PLAY_DMA0_REQUEST; + } else if (adr->status & AUDIO_STATUS_PLAY_DMA1_REQUEST) { + dma_to_fill = 1; + adr->status &= ~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; + } + adr->dma_xfer_requests[dma_to_fill].ptr = (void *)ubi32_rd->period_ptr; + adr->dma_xfer_requests[dma_to_fill].ctr = ubi32_rd->period_size; + adr->dma_xfer_requests[dma_to_fill].active = 1; +#ifdef CONFIG_SND_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 + adr->int_flags |= 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 audio_dev_regs *adr; + struct resource *res_adr; + 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, adr is the hardware driver base address + * and the tx and rx irqs are used to communicate with the + * hardware driver. + */ + res_adr = platform_get_resource(pdev, IORESOURCE_MEM, AUDIO_MEM_RESOURCE); + res_irq_tx = platform_get_resource(pdev, IORESOURCE_IRQ, AUDIO_TX_IRQ_RESOURCE); + res_irq_rx = platform_get_resource(pdev, IORESOURCE_IRQ, AUDIO_RX_IRQ_RESOURCE); + if (!res_adr || !res_irq_tx || !res_irq_rx) { + snd_printk(KERN_WARNING "Could not get resources"); + return -ENODEV; + } + + ubi32_priv->ar = (struct audio_regs *)res_adr->start; + ubi32_priv->tx_irq = res_irq_tx->start; + ubi32_priv->rx_irq = res_irq_rx->start; + ubi32_priv->irq_idx = pdata->inst_num; + ubi32_priv->adr = &(ubi32_priv->ar->adr[pdata->inst_num]); + + /* + * Check the version + */ + adr = ubi32_priv->adr; + if (adr->version != AUDIO_DEV_REGS_VERSION) { + snd_printk(KERN_WARNING "This audio_dev_reg 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 < adr->n_sample_rates; j++) { + if (rates[i] == adr->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_pcm_hw.rates = rate_mask; + snd_ubi32_pcm_hw.rate_min = rate_min; + snd_ubi32_pcm_hw.rate_max = rate_max; + ubi32_pcm_rates.count = adr->n_sample_rates; + ubi32_pcm_rates.list = (unsigned int *)adr->sample_rates; + ubi32_pcm_rates.mask = 0; + + for (i = 0; i < 32; i++) { + if (adr->channel_mask & (1 << i)) { + if (!snd_ubi32_pcm_hw.channels_min) { + snd_ubi32_pcm_hw.channels_min = i; + } + snd_ubi32_pcm_hw.channels_max = i; + } + } + snd_printk(KERN_INFO "Ubi32PCM: channels_min:%u channels_max:%u\n", + snd_ubi32_pcm_hw.channels_min, + snd_ubi32_pcm_hw.channels_max); + + if (adr->caps & AUDIONODE_CAP_BE) { + snd_ubi32_pcm_hw.formats |= SNDRV_PCM_FMTBIT_S16_BE; + } + if (adr->caps & AUDIONODE_CAP_LE) { + snd_ubi32_pcm_hw.formats |= SNDRV_PCM_FMTBIT_S16_LE; + } + + snd_printk(KERN_INFO "Ubi32PCM: rates:%08x min:%u max:%u count:%d fmts:%016llx (%s)\n", + snd_ubi32_pcm_hw.rates, + snd_ubi32_pcm_hw.rate_min, + snd_ubi32_pcm_hw.rate_max, + ubi32_pcm_rates.count, + snd_ubi32_pcm_hw.formats, + ubi32_priv->is_capture ? "capture" : "playback"); + + if (ubi32_priv->is_capture) { + ret = snd_pcm_new(ubi32_priv->card, "Ubi32 PCM", 0, 0, 1, &pcm); + } else { + 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; + + strcpy(pcm->name, "Ubi32-PCM"); + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 45*1024, 64*1024); + + if (ubi32_priv->is_capture) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ubi32_pcm_ops); + } else { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ubi32_pcm_ops); + } + + /* + * Start up the audio device + */ + adr->int_flags |= AUDIO_INT_FLAG_COMMAND; + adr->command = AUDIO_CMD_ENABLE; + snd_ubi32_vp_int_set(pcm); + + /* + * Request IRQ + */ + err = request_irq(ubi32_priv->rx_irq, snd_ubi32_pcm_interrupt, IRQF_SHARED | 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; + +} diff --git a/target/linux/ubicom32/files/sound/ubicom32/ubi32.h b/target/linux/ubicom32/files/sound/ubicom32/ubi32.h new file mode 100644 index 0000000000..f43a2150b4 --- /dev/null +++ b/target/linux/ubicom32/files/sound/ubicom32/ubi32.h @@ -0,0 +1,102 @@ +/* + * 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 . + */ + +#ifndef _UBI32_H +#define _UBI32_H + +#define SND_UBI32_DEBUG 0 // Debug flag + +#include +#include +#include +#include + +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; + + /* + * capture (1) or playback (0) + */ + int is_capture; + /* + * 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 audio_regs *ar; + struct audio_dev_regs *adr; + u32 irq_idx; + 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 diff --git a/target/linux/ubicom32/patches-2.6.30/100-ubicom32_support.patch b/target/linux/ubicom32/patches-2.6.30/100-ubicom32_support.patch index 04dec6bbd1..eca2740a8c 100644 --- a/target/linux/ubicom32/patches-2.6.30/100-ubicom32_support.patch +++ b/target/linux/ubicom32/patches-2.6.30/100-ubicom32_support.patch @@ -1,59015 +1,1741 @@ ---- /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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "crypto_ubicom32.h" -+#include -+ -+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; -+ } -+} +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -148,3 +148,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. + -+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; ++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. + -+ struct blkcipher_walk walk; -+ blkcipher_walk_init(&walk, dst, src, nbytes); -+ ret = blkcipher_walk_virt(desc, &walk); -+ if (ret) { -+ return ret; -+ } ++ To compile this driver as a module, choose M here: the ++ module will be called pasemi-rng. + -+ hw_crypto_lock(); -+ hw_crypto_check(); ++ If unsure, say Y. +--- a/drivers/char/hw_random/Makefile ++++ b/drivers/char/hw_random/Makefile +@@ -15,3 +15,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 +--- 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. + -+ hw_crypto_set_ctrl(uctx->ctrl | extra_flags); -+ aes_hw_set_key(uctx->key, uctx->key_len); ++config CRYPTO_AES_UBICOM32 ++ tristate "Ubicom32 AES implementation" ++ depends on CRYPTO_UBICOM32 ++ select CRYPTO_ALGAPI ++ help ++ This is the ubicom32 hardware AES implementation. + -+ 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; ++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. + -+ /* finish n/16 blocks */ -+ ecb_aes_crypt_loop(out, in, n); ++config CRYPTO_SHA1_UBICOM32 ++ tristate "Ubicom32 SHA1 implementation" ++ depends on CRYPTO_UBICOM32 ++ select CRYPTO_ALGAPI ++ help ++ This is the ubicom32 hardware SHA1 implementation. + -+ nbytes &= AES_BLOCK_SIZE - 1; -+ ret = blkcipher_walk_done(desc, &walk, nbytes); -+ } ++config CRYPTO_MD5_UBICOM32 ++ tristate "Ubicom32 MD5 implementation" ++ depends on CRYPTO_UBICOM32 ++ select CRYPTO_ALGAPI ++ help ++ This is the ubicom32 hardware MD5 implementation. + -+ hw_crypto_unlock(); -+ return ret; -+} + config ZCRYPT + tristate "Support for PCI-attached cryptographic adapters" + depends on S390 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -266,3 +266,10 @@ config GPIOMMC_CONFIGFS + help + This option automatically enables configfs support for gpiommc + if configfs is available. + -+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); -+} ++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 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -30,4 +30,5 @@ obj-$(CONFIG_MMC_S3C) += s3cmci.o + obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o + obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o + obj-$(CONFIG_GPIOMMC) += gpiommc.o ++obj-$(CONFIG_MMC_UBICOM32) += ubicom32sd.o + +--- a/drivers/mtd/devices/Kconfig ++++ b/drivers/mtd/devices/Kconfig +@@ -104,6 +104,31 @@ config M25PXX_USE_FAST_READ + help + This option enables FAST_READ access supported by ST M25Pxx. + ++config MTD_UBI32_NAND_SPI_ER ++ tristate "UBI32_NAND SPI-ER support" ++ help ++ This driver supports the Micron MT29F1G01 SPI-ER NAND flash chip ++ using the built in flash controller on the Ubicom32 architecture. ++ Partial page writes are not supported by this driver. + -+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); -+} ++config MTD_NAND_SPI_ER ++ tristate "NAND SPI-ER support" ++ help ++ This driver supports the Micron MT29F1G01 SPI-ER NAND flash chip ++ using a generic SPI bus. Partial page writes are supported ++ by this driver. + -+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, -+ } -+ } -+}; ++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. + -+#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" -+ ); -+} + config MTD_SLRAM + tristate "Uncached system RAM" + help +--- a/drivers/mtd/devices/Makefile ++++ b/drivers/mtd/devices/Makefile +@@ -16,3 +16,6 @@ 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 ++obj-$(CONFIG_MTD_NAND_SPI_ER) += nand-spi-er.o ++obj-$(CONFIG_MTD_UBI32_NAND_SPI_ER) += ubi32-nand-spi-er.o +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -2540,6 +2540,19 @@ 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" ++ depends on UBICOM32 ++ help ++ Gigabit Ethernet support for ubicom32 processors + -+#else ++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 when possible + -+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 # NETDEV_1000 + + # +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -272,3 +272,5 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o + obj-$(CONFIG_SFC) += sfc/ + + obj-$(CONFIG_WIMAX) += wimax/ + ++obj-$(CONFIG_UBICOM32_GMAC) += ubi32-eth.o +--- a/drivers/net/usb/asix.c ++++ b/drivers/net/usb/asix.c +@@ -319,14 +319,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; ++ } + -+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); + 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 { -+ cbc_aes_decrypt_loop(out, in, walk.iv, n); ++ ax_skb->data = packet; + } -+ } -+ -+ 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(); ++#else ++ ax_skb->data = packet; ++#endif + ax_skb->len = size; + ax_skb->data = packet; + skb_set_tail_pointer(ax_skb, size); +@@ -1125,13 +1144,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 +@@ -328,10 +328,10 @@ static inline void oprofile_end_trace(st + } + + static inline void +-__oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, +- unsigned long event, int is_kernel) ++__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); + unsigned long backtrace = oprofile_backtrace_depth; + + /* +@@ -353,7 +353,8 @@ __oprofile_add_ext_sample(unsigned long + void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, + unsigned long event, int is_kernel) + { +- __oprofile_add_ext_sample(pc, regs, event, 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) +@@ -361,7 +362,8 @@ void oprofile_add_sample(struct pt_regs + int is_kernel = !user_mode(regs); + unsigned long pc = profile_pc(regs); + +- __oprofile_add_ext_sample(pc, regs, event, is_kernel); ++ __oprofile_add_ext_sample_cpu(pc, regs, event, ++ is_kernel, smp_processor_id()); + } + + /* +--- a/drivers/pci/Makefile ++++ b/drivers/pci/Makefile +@@ -44,8 +44,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 +@@ -871,6 +871,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. + -+ ret = crypto_register_alg(&aes_alg); -+ if (ret) -+ goto aes_err; ++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. + -+ ret = crypto_register_alg(&ecb_aes_alg); -+ if (ret) -+ goto ecb_aes_err; ++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. + -+ ret = crypto_register_alg(&cbc_aes_alg); -+ if (ret) -+ goto cbc_aes_err; ++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. + -+out: -+ return ret; ++config SERIAL_UBI32_SERDES_CONSOLE ++ bool "Ubicom serial console support" ++ depends on SERIAL_UBI32_SERDES=y ++ select SERIAL_CORE_CONSOLE ++ default y + -+cbc_aes_err: -+ crypto_unregister_alg(&ecb_aes_alg); -+ecb_aes_err: -+ crypto_unregister_alg(&aes_alg); -+aes_err: -+ goto out; -+} ++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. + -+static void __exit aes_fini(void) -+{ -+ crypto_unregister_alg(&cbc_aes_alg); -+ crypto_unregister_alg(&ecb_aes_alg); -+ crypto_unregister_alg(&aes_alg); -+} ++config SERIAL_UBI32_MAILBOX_CONSOLE ++ bool "Ubicom mailbox console support" ++ depends on SERIAL_UBI32_MAILBOX=y ++ select SERIAL_CORE_CONSOLE ++ default y + -+module_init(aes_init); -+module_exit(aes_fini); + config SERIAL_SUNCORE + bool + depends on SPARC +--- a/drivers/serial/Makefile ++++ b/drivers/serial/Makefile +@@ -77,3 +77,6 @@ obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIA + 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 +--- 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. + -+MODULE_ALIAS("aes"); + 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 +--- a/drivers/uio/Kconfig ++++ b/drivers/uio/Kconfig +@@ -89,4 +89,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. + -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef __CRYPTO_DES_H__ -+#define __CRYPTO_DES_H__ ++ If you compile this as a module, it will be called uio_ubicom32ring + -+extern int crypto_des_check_key(const u8*, unsigned int, u32*); + endif +--- a/drivers/uio/Makefile ++++ b/drivers/uio/Makefile +@@ -5,3 +5,4 @@ obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdr + obj-$(CONFIG_UIO_SMX) += uio_smx.o + obj-$(CONFIG_UIO_AEC) += uio_aec.o + obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o ++obj-$(CONFIG_UIO_UBICOM32RING) += uio_ubicom32ring.o +--- 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; + -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include "crypto_ubicom32.h" ++ case USB_ENDPOINT_XFER_BULK: ++ if ((gadget->is_dualspeed) && (ep->maxpacket < 512)) ++ return 0; + } + + /* MATCH!! */ +--- 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 +@@ -12,7 +12,7 @@ config USB_MUSB_HDRC + depends on !SUPERH + select TWL4030_USB if MACH_OMAP_3430SDP + select USB_OTG_UTILS +- tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' ++ tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, 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 +@@ -30,6 +30,10 @@ ifeq ($(CONFIG_BF52x),y) + musb_hdrc-objs += blackfin.o + endif + ++ifeq ($(CONFIG_UBICOM32), y) ++ musb_hdrc-objs += ubi32_usb.o ++endif + -+spinlock_t crypto_ubicom32_lock; -+bool crypto_ubicom32_inited = false; -+volatile bool crypto_ubicom32_on = false; -+volatile unsigned long crypto_ubicom32_last_use; + 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 + #endif + ++#ifdef CONFIG_UBICOM32 ++#include ++#include ++extern void ubi32_usb_init(void); ++extern void ubi32_usb_int_clr(void); ++#endif + -+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; -+ } + #include "musb_core.h" + + +@@ -147,8 +154,37 @@ static inline struct musb *dev_to_musb(s + } + + /*-------------------------------------------------------------------------*/ ++#if defined(CONFIG_UBICOM32) + -+ /* 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc ++ * Load an endpoint's FIFO + */ -+#ifndef _CRYPTO_ARCH_UBICOM32_CRYPT_H -+#define _CRYPTO_ARCH_UBICOM32_CRYPT_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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) ++void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 wCount, const u8 *pSource) +{ -+ 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; -+} ++ void __iomem *fifo = hw_ep->fifo; + -+/* -+ * 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(); -+} ++ prefetch((u8 *)pSource); + -+/* -+ * 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; -+} ++ DBG(4, "%cX ep%d fifo %p count %d buf %p\n", ++ 'T', hw_ep->epnum, fifo, wCount, pSource); + -+/* -+ * 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(); -+ } -+} ++ usb_tio_write_fifo((u32)fifo, (u32)pSource, wCount); + -+/* -+ * 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. ++ * Unload an endpoint's FIFO + */ -+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) ++void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 wCount, u8 *pDest) +{ -+ 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"); ++ 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); +} -+#define UBICOM32_SEC_DUMP(a, b, c) hex_dump(a, b, c) + +-#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) ++#elif !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) + + /* + * Load an endpoint's FIFO +@@ -227,8 +263,7 @@ void musb_read_fifo(struct musb_hw_ep *h + readsb(fifo, dst, len); + } + } +- +-#endif /* normal PIO */ ++#endif /* !T6010 && !BLACKFIN */ + + + /*-------------------------------------------------------------------------*/ +@@ -874,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 -+#define UBICOM32_SEC_DUMP(a, b, c) ++ musb_writeb(regs, MUSB_POWER, MUSB_POWER_HSENAB ++ /* ENSUSPEND wedges tusb */ ++ /* | MUSB_POWER_ENSUSPEND */ ++ ); +#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 . -+ * Modified by Raimar Falke 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 -+ * Copyright (c) Gisle Sflensminde -+ * Copyright (C) 2001 Niels Mvller. -+ * Copyright (c) 2002 James Morris -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#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; -+ } + + musb->is_active = 0; + devctl = musb_readb(regs, MUSB_DEVCTL); +@@ -1081,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. +@@ -1240,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 +@@ -1256,6 +1299,11 @@ 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); +@@ -1276,14 +1324,27 @@ 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; + } + } -+ } -+ 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+#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 /* 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; + } +@@ -1393,12 +1454,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; + } + -+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; -+ } -+} + } else { + if (!musb->config->dyn_fifo) + status = ep_config_from_hw(musb); +@@ -1462,8 +1527,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; +@@ -1472,10 +1537,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 -+ -+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; + ++ 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); + +@@ -2210,6 +2282,10 @@ static struct platform_driver musb_drive + + static int __init musb_init(void) + { ++#ifdef CONFIG_UBICOM32 ++ ubi32_usb_init(); +#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; -+ } -+} + #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,12 @@ struct musb { + * queue until it completes or NAKs too much; then we try the next + * endpoint. + */ ++#ifdef CONFIG_UBICOM32 ++ struct musb_hw_ep *bulk_ep_in; ++ struct musb_hw_ep *bulk_ep_out; ++#else + struct musb_hw_ep *bulk_ep; +#endif + + struct list_head control; /* of musb_qh */ + struct list_head in_bulk; /* of musb_qh */ +--- a/drivers/usb/musb/musb_gadget.c ++++ b/drivers/usb/musb/musb_gadget.c +@@ -432,7 +432,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) { +@@ -448,7 +448,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); +@@ -584,10 +584,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; +- unsigned fifo_count = 0; +- u16 len = musb_ep->packet_sz; ++ u16 fifo_count = 0; ++ u16 len = 0; + -+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; ++ 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); + +@@ -726,7 +732,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); + } +@@ -745,10 +751,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; + -+ case DES3_EEE_DECRYPT: -+ cbc_des3_eee_decrypt_loop(uctx->key, out, in, iv, n); -+ break; + musb_ep_select(mbase, epnum); + + request = next_request(musb_ep); +@@ -1769,7 +1780,9 @@ int usb_gadget_register_driver(struct us + } + } + } +- ++#ifndef CONFIG_USB_MUSB_OTG ++ musb_pullup(musb, 1); +#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 -+# -+# -+# (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 . -+# -+# 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,234 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#define __ASM__ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#define __ASM__ -+#include -+ -+#ifndef RP -+#define RP A5 + 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 +@@ -160,7 +160,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); ++#ifndef CONFIG_UBICOM32 + txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_H_WZC_BITS; ++#else ++ txcsr |= (MUSB_TXCSR_TXPKTRDY & (~MUSB_TXCSR_H_WZC_BITS)); +#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+#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 . -+ * -+ * 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,348 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_ATOMIC_H -+#define _ASM_UBICOM32_ATOMIC_H -+ -+#include -+#include -+#include -+ -+/* -+ * 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. -+ */ -+ -+#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 -+ -+/* -+ * 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.h -@@ -0,0 +1,40 @@ -+/* -+ * arch/ubicom32/include/asm/audio.h -+ * Audio 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 . -+ */ -+ -+#ifndef _AUDIO_H -+#define _AUDIO_H -+ -+#include -+#include -+ -+/* -+ * Resource indices used to access IRQs via platform_get_resource -+ */ -+#define AUDIO_MEM_RESOURCE 0 -+#define AUDIO_TX_IRQ_RESOURCE 0 -+#define AUDIO_RX_IRQ_RESOURCE 1 -+ -+extern struct platform_device * __init audio_device_alloc(const char *driver_name, const char *node_name, const char *inst_name, int priv_size); -+ -+#define audio_device_priv(pdev) (((struct ubi32pcm_platform_data *)(((struct platform_device *)(pdev))->dev.platform_data))->priv_data) -+#endif ---- /dev/null -+++ b/arch/ubicom32/include/asm/audionode.h -@@ -0,0 +1,152 @@ -+/* -+ * audionode.h -+ * audionode and DMA descriptors -+ * -+ * Copyright © 2009 Ubicom Inc. . All rights reserved. -+ * -+ * This file contains confidential information of Ubicom, Inc. and your use of -+ * this file is subject to the Ubicom Software License Agreement distributed with -+ * this file. If you are uncertain whether you are an authorized user or to report -+ * any unauthorized use, please contact Ubicom, Inc. at +1-408-789-2200. -+ * Unauthorized reproduction or distribution of this file is subject to civil and -+ * criminal penalties. -+ * -+ */ -+#ifndef _AUDIONODE_H_ -+#define _AUDIONODE_H_ -+ -+#define AUDIO_INT_FLAG_MORE_SAMPLES 0x00000001 -+#define AUDIO_INT_FLAG_COMMAND 0x00000002 -+ -+/* -+ * Commands the Primary OS sends to the audio device -+ */ -+enum audio_command { -+ AUDIO_CMD_NONE, -+ AUDIO_CMD_START, -+ AUDIO_CMD_STOP, -+ AUDIO_CMD_PAUSE, -+ AUDIO_CMD_RESUME, -+ AUDIO_CMD_MUTE, -+ AUDIO_CMD_UNMUTE, -+ AUDIO_CMD_SETUP, -+ AUDIO_CMD_ENABLE, -+ AUDIO_CMD_DISABLE, -+}; -+ -+/* -+ * Flag bits passed in the registers -+ */ -+#define CMD_START_FLAG_LE (1 << 0) /* Use Little Endian Mode */ -+ -+/* -+ * Status bits that audio device can set to indicate reason -+ * for interrupting the Primary OS -+ */ -+#define AUDIO_STATUS_PLAY_DMA0_REQUEST (1 << 0) /* Audio device needs samples in DMA0 for playback */ -+#define AUDIO_STATUS_PLAY_DMA1_REQUEST (1 << 1) /* Audio device needs samples in DMA1 for playback */ -+ -+struct 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 ipAudio 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 u32_t active; /* Nonzero if data in ptr/ctr ready to be transferred */ -+ volatile void *ptr; /* Pointer to data to be transferred */ -+ volatile u32_t ctr; /* Counter: number of data units to transfer */ -+}; -+ -+#define AUDIONODE_CAP_BE (1 << 0) -+#define AUDIONODE_CAP_LE (1 << 1) -+ -+#define AUDIONODE_VERSION 7 -+struct audio_node { -+ struct devtree_node dn; -+ -+ /* -+ * Version of this node -+ */ -+ u32_t version; -+ -+ /* -+ * Pointer to the registers -+ */ -+ struct audio_regs *regs; -+}; -+ -+/* -+ * [OCM] Audio registers -+ * Registers exposed as part of our MMIO area -+ */ -+#define AUDIO_REGS_VERSION 7 -+struct audio_regs { -+ /* -+ * Version of this register set -+ */ -+ u32_t version; -+ -+ /* -+ * Interrupt status -+ */ -+ volatile u32_t int_status; -+ -+ /* -+ * Interrupt request -+ */ -+ volatile u32_t int_req; -+ -+ /* -+ * Current IRQ being serviced -+ */ -+ u32_t cur_irq; -+ -+ /* -+ * Maximum number of devices supported -+ */ -+ u32_t max_devs; -+ -+ /* -+ * [DDR] Device registers for each of the devices -+ */ -+ struct audio_dev_regs *adr; -+}; -+ -+#define AUDIO_DEV_REGS_VERSION 2 -+struct audio_dev_regs { -+ u32_t version; /* Version of this register set */ -+ -+ u8_t name[32]; /* Name of this driver */ -+ u32_t caps; /* Capabilities of this driver */ -+ const 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; /* A bit set in a particular position means we support this channel configuration */ -+ volatile u32_t int_flags; /* Reason for interrupting audio device */ -+ volatile enum audio_command command; /* Command from Primary OS */ -+ volatile u32_t flags; /* Flag bits for this command */ -+ volatile u32_t channels; /* Number of channels */ -+ volatile u32_t sample_rate; /* Sample rate */ -+ volatile u32_t status; /* Status bits sent from ipAudio to Primary OS */ -+ void *primary_os_buffer_ptr; /* -+ * Playback: Pointer to next sample to be removed from -+ * Primary OS sample buffer -+ * Capture: Pointer to where next sample will be inserted -+ * into Primary OS sample buffer -+ */ -+ -+ /* -+ * These are the transfer requests. They are used in alternating -+ * order so that when ipAudio 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 audio_dma dma_xfer_requests[2]; -+}; -+ -+#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 . -+ * -+ * 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,172 @@ -+/* -+ * 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 . -+ * -+ * 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 -+#include /* swab32 */ -+ -+#ifdef __KERNEL__ -+ -+#ifndef _LINUX_BITOPS_H -+#error only can be included directly -+#endif -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+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 -+ -+/* -+ * 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 -+#include -+#include -+ -+#include -+#include -+#include -+ -+#endif /* __KERNEL__ */ -+ -+#include -+#include -+#include -+ -+#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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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,95 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_BUG_H -+#define _ASM_UBICOM32_BUG_H -+ -+#include -+#include -+ -+#if defined(CONFIG_BUG) && defined(CONFIG_STOP_ON_BUG) -+ -+/* -+ * BUG() -+ * Ubicom specific version of the BUG() macro. -+ * -+ * This implementation performs a THREAD_STALL stopping all threads before -+ * calling panic. This enables a developer to see the "real" state of the -+ * machine (since panic alters the system state). We do the printf first -+ * because while it is slow, it does not alter system state (like -+ * interrupts). -+ * -+ * TODO: Implement the trap sequence used by other architectures. -+ */ -+#define BUG() do { \ -+ printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ -+ THREAD_STALL; \ -+ panic("BUG!"); \ -+} while (0) -+ -+ -+/* -+ * __WARN() -+ * WARN() using printk() for now. -+ * -+ * TODO: Implement the trap sequence used by other architectures. -+ */ -+#define __WARN() \ -+ do { \ -+ printk("WARN: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ -+ } while(0) -+ -+/* -+ * WARN_ON() -+ * Ubicom specific version of the WARN_ON macro. -+ * -+ * This implementation performs a printk for the WARN_ON() instead -+ * of faulting into the kernel and using report_bug(). -+ * -+ * TODO: Implement the trap sequence used by other architectures. -+ */ -+#define WARN_ON(x) ({ \ -+ int __ret_warn_on = !!(x); \ -+ if (__builtin_constant_p(__ret_warn_on)) { \ -+ if (__ret_warn_on) \ -+ __WARN(); \ -+ } else { \ -+ if (unlikely(__ret_warn_on)) \ -+ __WARN(); \ -+ } \ -+ unlikely(__ret_warn_on); \ -+}) -+ -+ -+#define HAVE_ARCH_BUG -+#define HAVE_ARCH_WARN_ON -+ -+#endif -+ -+#include -+ -+#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 . -+ * -+ * 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,33 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_BYTEORDER_H -+#define _ASM_UBICOM32_BYTEORDER_H -+ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_CACHECTL_H -+#define _ASM_UBICOM32_CACHECTL_H -+ -+#include -+ -+/* -+ * 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,111 @@ -+/* -+ * 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 . -+ * -+ * 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 -+ */ -+#include -+#include -+#include -+ -+#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. */ \ -+ mem_cache_control(DCCR_BASE, start, end, CCR_CTRL_FLUSH_ADDR); \ -+ mem_cache_control(ICCR_BASE, start, end, CCR_CTRL_INV_ADDR); \ -+} while (0) -+ -+#define flush_icache_range(start, end) \ -+do { \ -+ /* Flush the data cache and invalidate the I cache. */ \ -+ mem_cache_control(DCCR_BASE, start, end, CCR_CTRL_FLUSH_ADDR); \ -+ mem_cache_control(ICCR_BASE, start, end, CCR_CTRL_INV_ADDR); \ -+} 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) -+{ -+ if (cc == DCCR_BASE) -+ UBICOM32_LOCK(DCCR_LOCK_BIT); -+ else -+ UBICOM32_LOCK(ICCR_LOCK_BIT); -+ -+ 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" -+ ); -+ -+ if (cc == DCCR_BASE) -+ UBICOM32_UNLOCK(DCCR_LOCK_BIT); -+ else -+ UBICOM32_UNLOCK(ICCR_LOCK_BIT); -+ -+} -+ -+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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_CHECKSUM_H -+#define _ASM_UBICOM32_CHECKSUM_H -+ -+#include -+ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_CPU_H -+#define _ASM_UBICOM32_CPU_H -+ -+#include -+ -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_CPUTIME_H -+#define _ASM_UBICOM32_CPUTIME_H -+ -+#include -+ -+#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 -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_CURRENT_H -+#define _ASM_UBICOM32_CURRENT_H -+ -+#include -+ -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_DELAY_H -+#define _ASM_UBICOM32_DELAY_H -+ -+#include -+#include -+ -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_DEVICE_H -+#define _ASM_UBICOM32_DEVICE_H -+ -+#include -+ -+#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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_DIV64_H -+#define _ASM_UBICOM32_DIV64_H -+ -+#include -+ -+#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 . -+ * -+ * 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,328 @@ -+/* -+ * 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 . -+ * -+ * 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 -+#ifdef CONFIG_PCI -+ -+/* we implement the API below in terms of the existing PCI one, -+ * so include it */ -+#include -+/* need struct page definitions */ -+#include -+ -+static inline int -+dma_supported(struct device *dev, u64 mask) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ return pci_dma_supported(to_pci_dev(dev), mask); -+} -+ -+static inline int -+dma_set_mask(struct device *dev, u64 dma_mask) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ return pci_set_dma_mask(to_pci_dev(dev), dma_mask); -+} -+ -+static inline void * -+dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, -+ gfp_t flag) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ return pci_alloc_consistent(to_pci_dev(dev), size, dma_handle); -+} -+ -+static inline void -+dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, -+ dma_addr_t dma_handle) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ pci_free_consistent(to_pci_dev(dev), size, cpu_addr, dma_handle); -+} -+ -+static inline dma_addr_t -+dma_map_single(struct device *dev, void *cpu_addr, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ return pci_map_single(to_pci_dev(dev), cpu_addr, size, (int)direction); -+} -+ -+static inline void -+dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ pci_unmap_single(to_pci_dev(dev), dma_addr, size, (int)direction); -+} -+ -+static inline dma_addr_t -+dma_map_page(struct device *dev, struct page *page, -+ unsigned long offset, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ return pci_map_page(to_pci_dev(dev), page, offset, size, (int)direction); -+} -+ -+static inline void -+dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ pci_unmap_page(to_pci_dev(dev), dma_address, size, (int)direction); -+} -+ -+static inline int -+dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ return pci_map_sg(to_pci_dev(dev), sg, nents, (int)direction); -+} -+ -+static inline void -+dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ pci_unmap_sg(to_pci_dev(dev), sg, nhwentries, (int)direction); -+} -+ -+static inline void -+dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ pci_dma_sync_single_for_cpu(to_pci_dev(dev), dma_handle, -+ size, (int)direction); -+} -+ -+static inline void -+dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ pci_dma_sync_single_for_device(to_pci_dev(dev), dma_handle, -+ size, (int)direction); -+} -+ -+static inline void -+dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ pci_dma_sync_sg_for_cpu(to_pci_dev(dev), sg, nelems, (int)direction); -+} -+ -+static inline void -+dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, -+ enum dma_data_direction direction) -+{ -+ BUG_ON(dev->bus != &pci_bus_type); -+ -+ pci_dma_sync_sg_for_device(to_pci_dev(dev), sg, nelems, (int)direction); -+} -+ -+static inline int -+dma_mapping_error(struct device *dev, dma_addr_t dma_addr) -+{ -+ return pci_dma_mapping_error(to_pci_dev(dev), dma_addr); -+} -+ -+ -+#else -+ -+static inline int -+dma_supported(struct device *dev, u64 mask) -+{ -+ return 0; -+} -+ -+static inline int -+dma_set_mask(struct device *dev, u64 dma_mask) -+{ -+ BUG(); -+ return 0; -+} -+ -+static inline void * -+dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, -+ gfp_t flag) -+{ -+ BUG(); -+ return NULL; -+} -+ -+static inline void -+dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, -+ dma_addr_t dma_handle) -+{ -+ BUG(); -+} -+ -+static inline dma_addr_t -+dma_map_single(struct device *dev, void *cpu_addr, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+ return 0; -+} -+ -+static inline void -+dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+} -+ -+static inline dma_addr_t -+dma_map_page(struct device *dev, struct page *page, -+ unsigned long offset, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+ return 0; -+} -+ -+static inline void -+dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+} -+ -+static inline int -+dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+ return 0; -+} -+ -+static inline void -+dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+} -+ -+static inline void -+dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+} -+ -+static inline void -+dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+} -+ -+static inline void -+dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+} -+ -+static inline void -+dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, -+ enum dma_data_direction direction) -+{ -+ BUG(); -+} -+ -+static inline int -+dma_mapping_error(struct device *dev, dma_addr_t dma_addr) -+{ -+ return 0; -+} -+ -+#endif -+ -+/* Now for the API extensions over the pci_ one */ -+ -+#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) -+#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) -+#define dma_is_consistent(d, h) (1) -+ -+static inline int -+dma_get_cache_alignment(void) -+{ -+ /* no easy way to get cache size on all processors, so return -+ * the maximum possible, to be safe */ -+ return (1 << INTERNODE_CACHE_SHIFT); -+} -+ -+static inline void -+dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, -+ unsigned long offset, size_t size, -+ enum dma_data_direction direction) -+{ -+ /* just sync everything, that's all the pci API can do */ -+ dma_sync_single_for_cpu(dev, dma_handle, offset+size, direction); -+} -+ -+static inline void -+dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, -+ unsigned long offset, size_t size, -+ enum dma_data_direction direction) -+{ -+ /* just sync everything, that's all the pci API can do */ -+ dma_sync_single_for_device(dev, dma_handle, offset+size, direction); -+} -+ -+static inline void -+dma_cache_sync(struct device *dev, void *vaddr, size_t size, -+ enum dma_data_direction direction) -+{ -+ /* could define this in terms of the dma_cache ... operations, -+ * but if you get this on a platform, you should convert the platform -+ * to using the generic device DMA API */ -+ BUG(); -+} -+ -+#endif /* _ASM_UBICOM32_DMA_MAPPING_H */ ---- /dev/null -+++ b/arch/ubicom32/include/asm/elf.h -@@ -0,0 +1,173 @@ -+/* -+ * 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 . -+ * -+ * 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 -+#include -+ -+/* -+ * 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. -+ */ -+ -+/* -+ * ELF_CORE_COPY_TASK_REGS is needed to dump register state from multi threaded user projects. -+ */ -+extern int dump_task_regs(struct task_struct *, elf_gregset_t *); -+#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs) -+ -+/* 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 . -+ * -+ * 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 -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_ENTRY_H -+#define _ASM_UBICOM32_ENTRY_H -+ -+#include -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_ERRNO_H -+#define _ASM_UBICOM32_ERRNO_H -+ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_FB_H -+#define _ASM_UBICOM32_FB_H -+#include -+ -+#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 . -+ * -+ * 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 -+ -+#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 . -+ * -+ * 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 . -+ * -+ * 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/ftrace.h -@@ -0,0 +1 @@ -+/* empty */ ---- /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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_FUTEX_H -+#define _ASM_UBICOM32_FUTEX_H -+ -+#include -+ -+#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,453 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_GPIO_H -+#define _ASM_UBICOM32_GPIO_H -+ -+#include -+#include -+ -+#include -+ -+#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) -+#define GPIO_RI_14 ((32 * 8) + 14) -+#define GPIO_RI_15 ((32 * 8) + 15) -+ -+/* -+ * 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 -+ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_HARDIRQ_H -+#define _ASM_UBICOM32_HARDIRQ_H -+ -+#include -+#include -+ -+/* -+ * 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 /* 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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_IOCTL_H -+#define _ASM_UBICOM32_IOCTL_H -+ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_IOCTLS_H -+#define _ASM_UBICOM32_IOCTLS_H -+ -+#include -+ -+/* 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 . -+ * -+ * 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 -+#include -+ -+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 . -+ * -+ * 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,845 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#ifndef _ASM_UBICOM32_IP5000_H -+#define _ASM_UBICOM32_IP5000_H -+ -+#include -+ -+/* -+ * Inline assembly define -+ */ -+#define S(arg) #arg -+#define D(arg) S(arg) -+ -+/* -+ * Assembler include file -+ */ -+#include -+ -+/* -+ * 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 -+ -+ -+/* -+ * 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 . -+ * -+ * 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,96 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_IRQFLAGS_H -+#define _ASM_UBICOM32_IRQFLAGS_H -+ -+#include -+#include -+#if defined(CONFIG_SMP) -+#include -+#endif -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_IRQ_H -+#define _ASM_UBICOM32_IRQ_H -+ -+#include -+ -+/* -+ * 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 . -+ * -+ * 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 -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_KDEBUG_H -+#define _ASM_UBICOM32_KDEBUG_H -+ -+#include -+ -+#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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_LDSR_H -+#define _ASM_UBICOM32_LDSR_H -+ -+#include -+#include -+#include -+ -+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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_LOCAL_H -+#define _ASM_UBICOM32_LOCAL_H -+ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_MACHDEP_H -+#define _ASM_UBICOM32_MACHDEP_H -+ -+#include -+ -+/* 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 . -+ * -+ * 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/memory_map.h -@@ -0,0 +1,66 @@ -+/* -+ * arch/ubicom32/include/asm/memory_map.h -+ * Machine memory maps/ -+ * -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_MEMORY_MAP_H -+#define _ASM_UBICOM32_MEMORY_MAP_H -+ -+/* -+ * 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) -+ -+#define SDRAMSTART 0x40000000 -+ -+#define KERNELSTART (SDRAMSTART + 0x00400000) -+ -+#define FLASHSTART 0x60000000 -+ -+/* -+ * CODELOADER / OS_SYSCALL OCM Reservations -+ * Don't change these unless you know what you are doing. -+ */ -+#define CODELOADER_SIZE 0x30 -+#define CODELOADER_BEGIN OCMSTART /* Must be OCM start for gdb to work. */ -+#define CODELOADER_END (CODELOADER_BEGIN + CODELOADER_SIZE) -+ -+#define OS_SYSCALL_BEGIN CODELOADER_END /* system_call at this address */ -+#define OS_SYSCALL_SIZE (512 - CODELOADER_SIZE) -+#define OS_SYSCALL_END (OS_SYSCALL_BEGIN + OS_SYSCALL_SIZE) -+ -+#endif /* _ASM_UBICOM32_MEMORY_MAP_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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_MMAN_H -+#define _ASM_UBICOM32_MMAN_H -+ -+#include -+ -+#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 . -+ * -+ * 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 -+#include -+#include -+ -+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 -+ * -+ * 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 . -+ * -+ * 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,48 @@ -+/* -+ * 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 . -+ * -+ * 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) -+ -+#define ARCH_OOPS_MODULE_EXTRA(mod) \ -+ printk(KERN_INFO "%p %u OCM(%p %u)\n", \ -+ (mod)->module_core, (mod)->core_size, \ -+ (mod)->arch.ocm_inst, (mod)->arch.ocm_inst_size) -+#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 . -+ * -+ * 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 . -+ * -+ * 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 -+ -+#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 . -+ * -+ * 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 . -+ * -+ * 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,3 @@ -+#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 -+ * -+ * -+ * (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 . -+ * -+ * 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 . -+ * -+ * 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 -+ -+#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 -+ -+#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 -+#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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_PCI_H -+#define _ASM_UBICOM32_PCI_H -+ -+#include -+ -+/* 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/pcm_tio.h -@@ -0,0 +1,84 @@ -+/* -+ * arch/ubicom32/include/asm/pcm_tio.h -+ * Ubicom32 architecture PCM 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 . -+ */ -+#ifndef _ASM_UBICOM32_PCM_TIO_H -+#define _ASM_UBICOM32_PCM_TIO_H -+ -+#include -+ -+#define PCM_TIO_REGS_VERSION 2 -+struct pcm_tio_regs { -+ /* -+ * set this value to 1 to reload the parameters and restart the HRT -+ */ -+ u32_t reload; -+ -+ /* -+ * Pointers to the input and output buffers -+ */ -+ void *input_buf; -+ void *output_buf; -+ -+ /* -+ * Buffer size (see pcm_hrt.S for constraints) -+ */ -+ u32_t buffer_size; -+ -+ /* -+ * Current cycle. This variable increases every time half the buffer -+ * is consumed. -+ */ -+ u32_t cycle; -+ -+ /* -+ * Fields below this line are not accessed by the HRT. They are purely -+ * informational for the user of this TIO. -+ */ -+ -+ /* -+ * Version of this structure -+ */ -+ u32_t version; -+ -+ /* -+ * Number of channels supported -+ */ -+ u32_t channels; -+ -+ /* -+ * Maximum buffer size -+ */ -+ u32_t max_buffer_size; -+}; -+ -+/* -+ * Our device node -+ */ -+#define PCM_TIO_NODE_VERSION 1 -+struct pcm_tio_node { -+ struct devtree_node dn; -+ u32_t version; -+ struct pcm_tio_regs *regs; -+}; -+ -+#endif -+ ---- /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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_PERCPU_H -+#define _ASM_UBICOM32_PERCPU_H -+ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_PGALLOC_H -+#define _ASM_UBICOM32_PGALLOC_H -+ -+#include -+#include -+ -+#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 . -+ * -+ * 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 -+ -+//vic - this bit copied from m68knommu version -+#include -+#include -+#include -+ -+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/plio.h -@@ -0,0 +1,313 @@ -+/* -+ * plio.h -+ * PLIO defines. -+ * -+ * Copyright © 2009 Ubicom Inc. . 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 . -+ * This file contains confidential information of Ubicom, Inc. and your use of -+ * this file is subject to the Ubicom Software License Agreement distributed with -+ * this file. If you are uncertain whether you are an authorized user or to report -+ * any unauthorized use, please contact Ubicom, Inc. at +1-408-789-2200. -+ * Unauthorized reproduction or distribution of this file is subject to civil and -+ * criminal penalties. -+ */ -+ -+#ifndef __PLIO__H__ -+#define __PLIO__H__ -+ -+#include -+#include -+ -+#define PLIO_PORT RD -+#define PLIO_EXT_PORT RI -+ -+#define TRANSMIT_FIFO_WATERMARK 8 -+ -+/* -+ * PLIO non-blocking register definitions -+ */ -+#define PLIO_FN 2 -+ -+typedef struct { -+ unsigned : 10; -+ unsigned rxfifo_thread_enable: 1; /* allowed rxfifo thread enable */ -+ unsigned : 1; -+ unsigned rxfifo_thread: 4; /* allowed rxfifo thread access */ -+ unsigned : 4; -+ unsigned br_thread: 4; /* allowed blocking region thread access */ -+ unsigned fn_reset: 4; /* function reset bit vector */ -+ unsigned rxfifo_sel: 1; /* select between RXFIFO 0 and 1 */ -+ unsigned fn_sel: 3; /* select port function */ -+} plio_io_function_t; -+ -+typedef struct { -+ unsigned : 24; -+ unsigned pin:8; -+} plio_gpio_t; -+ -+typedef struct { -+ unsigned : 16; -+ unsigned txfifo_uf: 1; /* TXFIFO underflow */ -+ unsigned txfifo_wm: 1; /* TXFIFO watermark */ -+ unsigned rxfifo_of: 1; /* RXFIFO overflow */ -+ unsigned rxfifo_wm: 1; /* RXFIFO watermark */ -+ unsigned : 5; -+ unsigned lreg_int_addr_rd: 1; /* read from specified LREG address */ -+ unsigned lreg_int_addr_wr: 1; /* write to specified LREG address */ -+ unsigned extctl_int: 4; /* synchronized external interrupts */ -+ unsigned pfsm_int: 1; /* state machine */ -+} plio_intstat_t; -+ -+typedef struct { -+ unsigned txfifo_reset: 1; /* TXFIFO reset for int_set only */ -+ unsigned rxfifo_reset: 1; /* RXFIFO reset for int_set only */ -+ unsigned : 11; -+ unsigned idif_txfifo_flush: 1; /* flush TXFIFO and idif_txfifo */ -+ unsigned idif_rxfifo_flush: 1; /* flush RXFIFO and idif_rxfifo */ -+ unsigned pfsm_start: 1; /* input to fsm */ -+ unsigned txfifo_uf: 1; /* TXFIFO underflow */ -+ unsigned txfifo_wm: 1; /* TXFIFO watermark */ -+ unsigned rxfifo_of: 1; /* RXFIFO overflow */ -+ unsigned rxfifo_wm: 1; /* RXFIFO watermark */ -+ unsigned : 5; -+ unsigned lreg_int_addr_rd: 1; /* read from specified LREG address */ -+ unsigned lreg_int_addr_wr: 1; /* write to specified LREG address */ -+ unsigned extctl_int: 4; /* synchronized external interrupts */ -+ unsigned pfsm_int: 1; /* state machine */ -+} plio_intset_t; -+ -+typedef enum { -+ PLIO_PORT_MODE_D, -+ PLIO_PORT_MODE_DE, -+ PLIO_PORT_MODE_DI, -+ PLIO_PORT_MODE_DEI, -+ PLIO_PORT_MODE_DC, -+} plio_port_mode_t; -+ -+typedef enum { -+ PLIO_CLK_CORE, /* CORE CLK */ -+ PLIO_CLK_IO, /* IO CLK */ -+ PLIO_CLK_EXT, /* EXT CLK */ -+} plio_clk_src_t; -+typedef struct { -+ unsigned : 4; -+ unsigned edif_iaena_sel: 1; /* Input Address Enable Select */ -+ unsigned edif_iaclk_sel: 1; /* Input Address Clock Select */ -+ unsigned edif_iald_inv: 1; /* Input Address Strobe Invert */ -+ unsigned edif_idclk_sel: 1; /* Input Data Clock Select */ -+ unsigned edif_idld_inv: 1; /* Input Data Strobe Invert */ -+ unsigned edif_ds: 3; /* specify IDR and ODR data shift */ -+ unsigned edif_cmp_mode: 1; /* configure IDR comparator output */ -+ unsigned edif_idena_sel: 1; /* Input Data Enable Select */ -+ unsigned ecif_extclk_ena: 1; /* plio_extctl output select */ -+ unsigned idif_tx_fifo_cmd_sel: 1; /* select pfsm_cmd data word position */ -+ unsigned ptif_porti_cfg: 2; /* select port I pin configuration */ -+ unsigned ptif_portd_cfg: 3; /* select port D pin configuration */ -+ plio_port_mode_t ptif_port_mode: 3; /* select other plio ports */ -+ unsigned icif_clk_plio_ext_inv: 1; /* invert external plio clock when set */ -+ unsigned icif_rst_plio: 1; /* reset plio function and io fifos */ -+ plio_clk_src_t icif_clk_src_sel: 2; /* select plio clock source */ -+ unsigned pfsm_prog: 1; /* enable pfsm programming */ -+ unsigned pfsm_cmd: 3; /* software input to pfsm */ -+} plio_fctl0_t; -+ -+typedef struct { -+ unsigned : 2; -+ unsigned idif_byteswap_tx: 3; /* swap TXFIFO byte order */ -+ unsigned idif_byteswap_rx: 3; /* swap RXFIFO byte order */ -+ unsigned : 1; -+ unsigned lreg_ena: 1; /* enable local register map */ -+ unsigned lreg_addr_fifo_cmp_ena: 1; /* enable a specific LREG address from/to TX/RX fifos */ -+ unsigned lreg_addr_fifo_cmp: 5; /* LREG address routed from/to TX/RX fifos */ -+ unsigned : 1; -+ unsigned dcod_iald_idld_sel: 2; /* select address/data strobes */ -+ unsigned dcod_rw_src_sel: 1; /* select LREG strobe source */ -+ unsigned dcod_rd_sel: 5; /* select read strobe source */ -+ unsigned dcod_wr_sel: 5; /* select write strobe source */ -+ unsigned dcod_rd_lvl: 1; /* select active level of read strobe */ -+ unsigned dcod_wr_lvl: 1; /* select active level of read strobe */ -+} plio_fctl1_t; -+ -+typedef struct { -+ unsigned icif_eclk_div: 16; /* external plio clock divider */ -+ unsigned icif_iclk_div: 16; /* internal plio clock divider */ -+} plio_fctl2_t; -+ -+typedef struct { -+ unsigned : 27; -+ unsigned pfsm_state: 5; /* current pfsm state */ -+} plio_stat_0_t; -+ -+typedef struct { -+ unsigned : 3; -+ unsigned lreg_r_int_addr: 5; -+ unsigned : 11; -+ unsigned lreg_w_int_addr: 5; -+ unsigned lreg_w_int_data: 8; -+} plio_stat_1_t; -+ -+typedef struct { -+ unsigned : 32; -+} plio_stat_2_t; -+ -+typedef struct { -+ unsigned tx: 16; -+ unsigned rx: 16; -+} plio_io_fifo_wm_t, plio_io_fifo_lvl_t; -+ -+ -+/* plio blocking region register definitions -+ */ -+typedef struct { -+ unsigned ns1: 5; -+ unsigned ic1: 7; -+ unsigned ec1: 4; -+ unsigned ns0: 5; -+ unsigned ic0: 7; -+ unsigned ec0: 4; -+} plio_sram_t; -+ -+typedef struct { -+ unsigned : 2; -+ unsigned s9: 3; -+ unsigned s8: 3; -+ unsigned s7: 3; -+ unsigned s6: 3; -+ unsigned s5: 3; -+ unsigned s4: 3; -+ unsigned s3: 3; -+ unsigned s2: 3; -+ unsigned s1: 3; -+ unsigned s0: 3; -+} plio_grpsel_t; -+ -+typedef struct { -+ unsigned s7: 4; -+ unsigned s6: 4; -+ unsigned s5: 4; -+ unsigned s4: 4; -+ unsigned s3: 4; -+ unsigned s2: 4; -+ unsigned s1: 4; -+ unsigned s0: 4; -+} plio_cs_lut_t; -+ -+typedef struct { -+ unsigned lut3: 8; -+ unsigned lut2: 8; -+ unsigned lut1: 8; -+ unsigned lut0: 8; -+} plio_extctl_t; -+ -+typedef struct { -+ plio_grpsel_t grpsel[4]; -+ u16_t cv[16]; -+ plio_cs_lut_t cs_lut[4]; -+ plio_extctl_t extctl_o_lut[8]; -+} plio_pfsm_t; -+ -+typedef struct { -+ u32_t odr_oe_sel; -+ u32_t odr_oe; -+ u32_t cmp; -+ u32_t ncmp; -+ u32_t cmp_mask; -+} plio_edif_t; -+ -+typedef enum { -+ PLIO_ECIF_CLK_OUT = 9, -+ PLIO_ECIF_IALD = 9, -+ PLIO_ECIF_CLK_IN = 8, -+ PLIO_ECIF_IDLD = 8, -+ PLIO_ECIF_INT = 2, -+} plio_ecif_output_t; -+ -+typedef struct { -+ u32_t bypass_sync; -+ u32_t ift; -+ u32_t output_type; -+ u32_t output_ena; -+ u32_t output_lvl; -+} plio_ecif_t; -+ -+typedef struct { -+ u32_t idr_addr_pos_mask; -+ u32_t reserved; -+ u32_t lreg_bar; -+} plio_dcod_t; -+ -+typedef struct { -+ u32_t addr_rd_ena; -+ u32_t addr_wr_ena; -+ u32_t addr_rd_int_ena; -+ u32_t addr_wr_int_ena; -+} plio_lcfg_t; -+ -+ -+/* -+ * PLIO configuration -+ */ -+typedef struct { -+ plio_fctl0_t fctl0; -+ plio_fctl1_t fctl1; -+ plio_fctl2_t fctl2; -+} plio_fctl_t; -+ -+typedef struct { -+ plio_pfsm_t pfsm; -+ plio_edif_t edif; -+ plio_ecif_t ecif; -+ plio_dcod_t dcod; -+ plio_lcfg_t lcfg; -+} plio_config_t; -+ -+typedef struct { -+ plio_io_function_t function; -+ plio_gpio_t gpio_ctl; -+ plio_gpio_t gpio_out; -+ plio_gpio_t gpio_in; -+ plio_intstat_t intstat; -+ plio_intstat_t intmask; -+ plio_intset_t intset; -+ plio_intstat_t intclr; -+ unsigned tx_lo; -+ unsigned tx_hi; -+ unsigned rx_lo; -+ unsigned rx_hi; -+ plio_fctl0_t fctl0; -+ plio_fctl1_t fctl1; -+ plio_fctl2_t fctl2; -+ plio_stat_0_t stat0; -+ plio_stat_1_t stat1; -+ plio_stat_2_t stat2; -+ plio_io_fifo_wm_t fifo_wm; -+ plio_io_fifo_lvl_t fifo_lvl; -+} plio_nbr_t; -+ -+typedef struct { -+ u32_t pfsm_sram[256]; -+ plio_config_t config; -+} plio_br_t; -+ -+#define PLIO_NBR ((plio_nbr_t *)(PLIO_PORT)) -+#define PLIO_BR ((plio_br_t *)((PLIO_PORT + IO_PORT_BR_OFFSET))) -+#define PEXT_NBR ((plio_nbr_t *)(PLIO_EXT_PORT)) -+ -+extern void plio_init(const plio_fctl_t *plio_fctl, const plio_config_t *plio_config, const plio_sram_t plio_sram_cfg[], int sram_cfg_size); -+ -+#endif // __PLIO__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 . -+ * -+ * 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 -+ -+#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 . -+ * -+ * 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 . -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 void processor_ocm(unsigned long *socm, unsigned long *eocm); -+extern void processor_dram(unsigned long *sdram, unsigned long *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/profilesample.h -@@ -0,0 +1,44 @@ -+/* -+ * arch/ubicom32/mach-common/profile.h -+ * Private data for the profile module -+ * -+ * (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 . -+ * -+ */ -+ -+ -+#ifndef _PROFILESAMPLE_H_ -+#define _PROFILESAMPLE_H_ -+ -+/* -+ * a sample taken by the ipProfile package for sending to the profilertool -+ */ -+struct profile_sample { -+ unsigned int pc; /* PC value */ -+ unsigned int a5; /* a5 contents for parent of leaf function */ -+ unsigned int parent; /* return address from stack, to find the caller */ -+ unsigned int latency; /* CPU clocks since the last message dispatch in this thread (thread 0 ony for now) */ -+ unsigned short active; /* which threads are active - for accurate counting */ -+ unsigned short d_blocked; /* which threads are blocked due to D cache misses */ -+ unsigned short i_blocked; /* which threads are blocked due to I cache misses */ -+ unsigned char cond_codes; /* for branch prediction */ -+ unsigned char thread; /* I-blocked, D-blocked, 4-bit thread number */ -+}; -+ -+#endif ---- /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 . -+ * -+ * 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 . -+ * -+ * 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 -+ -+/* -+ * 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 . -+ * -+ * 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 -+/* -+ * 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 -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_RESOURCE_H -+#define _ASM_UBICOM32_RESOURCE_H -+ -+#include -+ -+#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 . -+ */ -+#ifndef _ASM_UBICOM32_RING_TIO_H -+#define _ASM_UBICOM32_RING_TIO_H -+ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SCATTERLIST_H -+#define _ASM_UBICOM32_SCATTERLIST_H -+ -+#include -+#include -+ -+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 . -+ */ -+#ifndef _ASM_UBICOM32_SD_TIO_H -+#define _ASM_UBICOM32_SD_TIO_H -+ -+#include -+ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SECTIONS_H -+#define _ASM_UBICOM32_SECTIONS_H -+ -+#include -+ -+#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 . -+ * -+ * 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 . -+ * -+ * 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 -+#include -+#include -+#include -+ -+#include -+#include -+ -+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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SIGCONTEXT_H -+#define _ASM_UBICOM32_SIGCONTEXT_H -+ -+#include -+ -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SIGINFO_H -+#define _ASM_UBICOM32_SIGINFO_H -+ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SIGNAL_H -+#define _ASM_UBICOM32_SIGNAL_H -+ -+#include -+ -+/* 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 -+ -+#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 -+#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,87 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SMP_H -+#define _ASM_UBICOM32_SMP_H -+ -+#ifndef CONFIG_SMP -+#error you should not include smp.h if smp is off -+#endif -+ -+#ifndef ASSEMBLY -+#include -+#include -+#include -+#include -+ -+typedef unsigned long address_t; -+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(const struct cpumask *mask); -+extern void smp_set_affinity(unsigned int irq, const struct cpumask *dest); -+extern void arch_send_call_function_single_ipi(int cpu); -+#define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask -+extern void arch_send_call_function_ipi_mask(const struct cpumask *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,87 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SOCKET_H -+#define _ASM_UBICOM32_SOCKET_H -+ -+#include -+ -+/* 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 -+ -+#define SO_TIMESTAMPING 37 -+#define SCM_TIMESTAMPING SO_TIMESTAMPING -+ -+#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 . -+ * -+ * 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,296 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SPINLOCK_H -+#define _ASM_UBICOM32_SPINLOCK_H -+ -+#include -+#include -+#include -+ -+/* -+ * __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_read_lock_flags(lock, flags) __raw_read_lock(lock) -+#define __raw_write_lock_flags(lock, flags) __raw_write_lock(lock) -+ -+#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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_STATFS_H -+#define _ASM_UBICOM32_STATFS_H -+ -+#include -+ -+#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 . -+ * -+ * 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,40 @@ -+/* -+ * 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 . -+ * -+ * 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); -+ -+#define __HAVE_ARCH_MEMMOVE -+extern void * memmove(void *to, const void *from, size_t len); -+ -+#endif /* _ASM_UBICOM32_STRING_H */ ---- /dev/null -+++ b/arch/ubicom32/include/asm/swab.h -@@ -0,0 +1,45 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_BYTEORDER_H -+#define _ASM_UBICOM32_BYTEORDER_H -+ -+#include -+ -+#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 */ -+ -+#endif /* _ASM_UBICOM32_BYTEORDER_H */ ---- /dev/null -+++ b/arch/ubicom32/include/asm/switch-dev.h -@@ -0,0 +1,51 @@ -+/* -+ * arch/ubicom32/include/asm/switch-dev.h -+ * generic Ethernet switch 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SWITCH_DEV_H -+#define _ASM_UBICOM32_SWITCH_DEV_H -+ -+#define SWITCH_DEV_FLAG_HW_RESET 0x01 -+#define SWITCH_DEV_FLAG_SW_RESET 0x02 -+ -+struct switch_core_platform_data { -+ /* -+ * See flags above -+ */ -+ u32_t flags; -+ -+ /* -+ * GPIO to use for nReset -+ */ -+ int pin_reset; -+ -+ /* -+ * Name of this switch -+ */ -+ const char *name; -+}; -+ -+#endif /* _ASM_UBICOM32_SWITCH_DEV_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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_SYSTEM_H -+#define _ASM_UBICOM32_SYSTEM_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_TERMBITS_H -+#define _ASM_UBICOM32_TERMBITS_H -+ -+#include -+ -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_TERMIOS_H -+#define _ASM_UBICOM32_TERMIOS_H -+ -+#include -+#include -+ -+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,51 @@ -+/* -+ * 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 . -+ * -+ * 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 -+ * -+ * Note that we don't need to mask the result as bits 6 through 31 of the -+ * ROSR are zeroes. -+ */ -+.macro thread_get_self reg -+ lsr.4 \reg, ROSR, #2 -+.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,320 @@ -+/* -+ * 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 . -+ * -+ * 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 -+#include -+ -+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; -+ -+ /* -+ * Note that ROSR has zeroes in bits 6 through 31 and so we don't need -+ * to do any additional bit masking here. -+ */ -+ asm ( -+ "lsr.4 %0, ROSR, #2 \n\t" -+ : "=d" (result) -+ : -+ : "cc" -+ ); -+ -+ return result; -+} -+ -+/* -+ * 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 -+ * -+ * Note that we don't need to mask the result as bits 6 through 31 of the -+ * ROSR are zeroes. -+ */ -+".macro thread_get_self reg \n\t" -+" lsr.4 \\reg, ROSR, #2 \n\t" -+".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 -+ -+#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 . -+ * -+ * 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 -+ -+/* -+ * 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< 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<. -+ * -+ * 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 -+ * Copyright (C) 2000-2002, Greg Ungerer -+ * -+ * 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 . -+ * -+ * 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 -+ -+/* -+ * 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 . -+ * -+ * 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 -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_TOPOLOGY_H -+#define _ASM_UBICOM32_TOPOLOGY_H -+ -+#include -+ -+#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 . -+ * -+ * 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 . -+ * -+ * 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 -+ -+#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 . -+ * -+ * 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 -+#include -+#include -+ -+#include -+ -+#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 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/uart_tio.h -@@ -0,0 +1,126 @@ -+/* -+ * arch/ubicom32/include/asm/uart_tio.h -+ * Ubicom32 architecture UART 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 . -+ */ -+#ifndef _ASM_UBICOM32_UART_TIO_H -+#define _ASM_UBICOM32_UART_TIO_H -+ -+#include -+ -+#define UARTTIO_RX_FIFO_SIZE 16 -+#define UARTTIO_TX_FIFO_SIZE 16 -+ -+/* -+ * Interrupt flags -+ */ -+#define UARTTIO_UART_INT_RX 0x00000001 // set when a character has been recevied (TODO: add watermark) -+#define UARTTIO_UART_INT_RXOVF 0x00000002 // set when the receive buffer has overflowed -+#define UARTTIO_UART_INT_RXFRAME 0x00000004 // set when there has been a framing error -+ -+#define UARTTIO_UART_INT_TX 0x00000100 // set every time a character is transmitted -+#define UARTTIO_UART_INT_TXBE 0x00000200 // set when the transmit buffer is empty (TODO: add watermark) -+ -+#define UARTTIO_UART_FLAG_ENABLED 0x80000000 -+#define UARTTIO_UART_FLAG_SET_RATE 0x00000001 // set to update baud rate -+#define UARTTIO_UART_FLAG_RESET 0x00000002 // set to reset the port -+struct uarttio_uart { -+ volatile u32_t flags; -+ -+ volatile u32_t baud_rate; -+ volatile u32_t current_baud_rate; -+ u32_t bit_time; -+ -+ /* -+ * Modem status register -+ */ -+ volatile u32_t status; -+ -+ /* -+ * Interrupt registers -+ */ -+ volatile u32_t int_mask; -+ volatile u32_t int_flags; -+ -+ /* -+ * Ports and pins -+ */ -+ u32_t rx_port; -+ u32_t tx_port; -+ -+ u8_t rx_pin; -+ u8_t tx_pin; -+ -+ /* -+ * Configuration Data -+ */ -+ u8_t rx_bits; -+ u8_t rx_stop_bits; -+ u8_t tx_bits; -+ u8_t tx_stop_bits; -+ -+ /* -+ * RX state machine data -+ */ -+ u32_t rx_timer; -+ u32_t rx_bit_pos; -+ u32_t rx_byte; -+ u32_t rx_fifo_head; -+ u32_t rx_fifo_tail; -+ u32_t rx_fifo_size; -+ -+ /* -+ * TX state machine data -+ */ -+ u32_t tx_timer; -+ u32_t tx_bit_pos; -+ u32_t tx_byte; -+ u32_t tx_fifo_head; -+ u32_t tx_fifo_tail; -+ u32_t tx_fifo_size; -+ -+ /* -+ * FIFOs -+ */ -+ u8_t rx_fifo[UARTTIO_RX_FIFO_SIZE]; -+ u8_t tx_fifo[UARTTIO_TX_FIFO_SIZE]; -+}; -+ -+#define UARTTIO_VP_VERSION 1 -+struct uarttio_regs { -+ u32_t version; -+ -+ u32_t thread; -+ -+ u32_t max_uarts; -+ -+ struct uarttio_uart uarts[0]; -+}; -+ -+#define UARTTIO_NODE_VERSION 1 -+struct uarttio_node { -+ struct devtree_node dn; -+ -+ u32_t version; -+ struct uarttio_regs *regs; -+ u32_t regs_sz; -+}; -+ -+#endif /* _ASM_UBICOM32_UART_TIO_H */ ---- /dev/null -+++ b/arch/ubicom32/include/asm/ubi32-cs4384.h -@@ -0,0 +1,52 @@ -+/* -+ * 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 . -+ */ -+#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,54 @@ -+/* -+ * 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 . -+ */ -+#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 audio node/inst -+ */ -+ const char *node_name; -+ const char *inst_name; -+ int inst_num; -+ -+ /* -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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,128 @@ -+/* -+ * 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 . -+ * -+ * 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. -+ * - DCCR_LOCK_BIT - Used to limit access to the DCCR cache control peripheral -+ * - ICCR_LOCK_BIT - Used to limit access to the ICCR cache control peripheral -+ * - 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 -+#define ICCR_LOCK_BIT 27 -+#define DCCR_LOCK_BIT 26 -+ -+#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 -+ -+#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 . -+ */ -+#ifndef _ASM_UBICOM32_UBICOM32FB_H -+#define _ASM_UBICOM32_UBICOM32FB_H -+ -+#include -+ -+/* -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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,38 @@ -+/* -+ * 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 . -+ */ -+#ifndef _ASM_UBICOM32_UBICOM32_LCD_H -+#define _ASM_UBICOM32_UBICOM32_LCD_H -+ -+#include -+ -+struct ubicom32lcd_platform_data { -+ int pin_cs; -+ int pin_rs; -+ int pin_rd; -+ int pin_wr; -+ int pin_reset; -+ int data_shift; -+ struct ubicom32_io_port *port_data; -+}; -+ -+#endif /* _ASM_UBICOM32_UBICOM32_LCD_H */ ---- /dev/null -+++ b/arch/ubicom32/include/asm/ubicom32lcdpower.h -@@ -0,0 +1,39 @@ -+/* -+ * arch/ubicom32/include/asm/ubicom32lcdpower.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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_UBICOM32_LCDPOWER_H -+#define _ASM_UBICOM32_UBICOM32_LCDPOWER_H -+ -+struct ubicom32lcdpower_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_LCDPOWER_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 . -+ */ -+ -+#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 . -+ */ -+#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 . -+ * -+ * 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 -+ * -+ * -+ * (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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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 . -+ * -+ * 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 -+ -+#include -+#include -+#include -+ -+#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 . -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_USER_H -+#define _ASM_UBICOM32_USER_H -+ -+#include -+#include -+/* -+ * Adapted from -+ * -+ * 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 . -+ * -+ * 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 -+ -+#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 -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#ifndef _ASM_UBICOM32_VGA_H -+#define _ASM_UBICOM32_VGA_H -+ -+#include -+ -+/* -+ * 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 -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _ASM_UBICOM32_XOR_H -+#define _ASM_UBICOM32_XOR_H -+ -+#include -+ -+#endif /* _ASM_UBICOM32_XOR_H */ ---- /dev/null -+++ b/arch/ubicom32/Kconfig -@@ -0,0 +1,403 @@ -+# -+# 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 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 -+ -+config UBICOM32_PLIO -+ bool -+ default n -+ -+menu "Processor type and features" -+ -+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 OLD_40400010_SYSTEM_CALL -+ bool "Provide old system call interface at 0x40400010" -+ default y -+ help -+ Provides the old system call interface, does not affect the -+ new system_call interface. -+ -+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" -+ -+config MIN_RAMSIZE -+ hex "Minimum Size of RAM (in bytes)" -+ range 0x01000000 0x08000000 -+ default "0x02000000" -+ help -+ Define the minimum acceptable size of the system -+ RAM. Must be at least 16MB (0x01000000) -+ -+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 -+ -+source "arch/ubicom32/mach-common/Kconfig.switch" -+ -+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,129 @@ -+menu "Kernel hacking" -+ -+config TRACE_IRQFLAGS_SUPPORT -+ def_bool y -+ -+config DEBUG_VERBOSE -+ bool "Verbose fault messages" -+ default y -+ select PRINTK -+ help -+ When a program crashes due to an exception, or the kernel detects -+ an internal error, the kernel can print a not so brief message -+ explaining what the problem was. This debugging information is -+ useful to developers and kernel hackers when tracking down problems, -+ but mostly meaningless to other people. This is always helpful for -+ debugging but serves no purpose on a production system. -+ Most people should say N here. -+ -+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,161 @@ -+/* -+ * 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 . -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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); -+ -+ 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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 . -+ * -+ * 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 -+#include -+#include -+#include -+#include -+ -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+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,273 @@ -+/* -+ * arch/ubicom32/kernel/head.S -+ * -+ * -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#define __ASM__ -+#include -+ -+ -+#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 - Old 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 - KERNELSTART) -+ -+.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 0x3FFC0030 - ~0x3FFC0200 -+ * I_RANGE2 / D_RANGE2 - slab area -+ * typically 0x40A00000 - ~0x44000000 -+ * I_RANGE3 -+ * old system call interface if enabled. -+ * -+ * D_RANGE3, D_RANGE4 - 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(SDRAMSTART + CONFIG_MIN_RAMSIZE-4) -+ lea.4 SRC_AN, %lo(SDRAMSTART + CONFIG_MIN_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(__syscall_text_run_begin) -+ lea.4 SRC_AN, %lo(__syscall_text_run_begin)(SRC_AN) -+ move.4 I_RANGE1_LO, SRC_AN -+ moveai SRC_AN, #%hi(__syscall_text_run_end) -+ lea.4 SRC_AN, %lo(__syscall_text_run_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 -+ -+#ifdef CONFIG_OLD_40400010_SYSTEM_CALL -+ ;; create a small hole for old syscall location -+ moveai SRC_AN, #%hi(0x40400000) -+ lea.4 I_RANGE3_LO, 0x10(SRC_AN) -+ lea.4 I_RANGE3_HI, 0x14(SRC_AN) -+#endif -+ ;; 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 -+#ifdef CONFIG_OLD_40400010_SYSTEM_CALL -+ move.4 I_RANGE3_EN, #-1 -+#else -+ move.4 I_RANGE3_EN, #0 -+#endif -+ ;; 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 .syscall_text -+; -+ moveai DST_AN, #%hi(__syscall_text_run_end) -+ lea.4 TMP_DN, %lo(__syscall_text_run_end)(DST_AN) -+ moveai DST_AN, #%hi(__syscall_text_run_begin) -+ lea.4 DST_AN, %lo(__syscall_text_run_begin)(DST_AN) -+ moveai SRC_AN, #%hi(__syscall_text_load_begin) -+ lea.4 SRC_AN, %lo(__syscall_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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+///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,597 @@ -+/* -+ * arch/ubicom32/kernel/irq.c -+ * Ubicom32 architecture IRQ support. -+ * -+ * (C) Copyright 2009, Ubicom, Inc. -+ * (C) Copyright 2007, Greg Ungerer -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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. -+ * affinity container allocated at boot -+ */ -+static void __irq_set_affinity(unsigned int irq, const struct cpumask *dest) -+{ -+ smp_set_affinity(irq, dest); -+ cpumask_copy(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 = current_thread_info(); -+ -+ IRQ_DECLARE_MEASUREMENT; -+ -+ /* -+ * Mark that we are inside of an interrupt and -+ * that interrupts are disabled. -+ */ -+ oldregs = set_irq_regs(regs); -+ ti->interrupt_nesting++; -+ trace_hardirqs_off(); -+ irq_kernel_stack_check(irq, regs); -+ -+ /* -+ * Start the interrupt sequence -+ */ -+ irq_enter(); -+ -+ /* -+ * Execute the IRQ handler and any pending SoftIRQ requests. -+ */ -+ BUG_ON(!irqs_disabled()); -+ IRQ_MEASUREMENT_START(); -+ __do_IRQ(irq); -+ IRQ_MEASUREMENT_END(irq); -+ BUG_ON(!irqs_disabled()); -+ -+ /* -+ * TODO: Since IRQ's are disabled when calling irq_exit() -+ * modify Kconfig to set __ARCH_IRQ_EXIT_IRQS_DISABLED flag. -+ * This will slightly improve performance by enabling -+ * softirq handling to avoid disabling/disabled interrupts. -+ */ -+ IRQ_MEASUREMENT_START(); -+ irq_exit(); -+ IRQ_MEASUREMENT_END(NR_IRQS); -+ BUG_ON(!irqs_disabled()); -+ -+ /* -+ * Outside of an interrupt (or nested exit). -+ */ -+ set_irq_regs(oldregs); -+ trace_hardirqs_on(); -+ 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,1185 @@ -+/* -+ * 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 . -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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) { -+ /* -+ * No CPU to deliver to so just leave -+ * the interrupt unmasked and increase -+ * the backout count. We will eventually -+ * return and deliver it again. -+ */ -+ ldsr_unmask_vector(vector); -+ ldsr_interrupt.backout++; -+ continue; -+ } -+ 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 . -+# -+# 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 -+ * 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,88 @@ -+/* -+ * arch/ubicom32/kernel/os_node.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 . -+ * -+ */ -+#include "linux/types.h" -+#include "linux/linkage.h" -+#include "linux/uts.h" -+#include "linux/utsrelease.h" -+#include "linux/version.h" -+#include -+#include -+#include -+ -+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; -+ unsigned long expected_os_syscall_begin; -+ unsigned long expected_os_syscall_end; -+}; -+ -+ -+extern void __os_syscall_begin; -+extern void __os_syscall_end; -+/* -+ * 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 = 0x10002, -+ .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 = KERNELSTART, -+ .expected_ram_end = SDRAMSTART + CONFIG_MIN_RAMSIZE, -+ .arch_version = UBICOM32_ARCH_VERSION, -+ .expected_os_syscall_begin = (unsigned long)&__os_syscall_begin, -+ .expected_os_syscall_end = (unsigned long)&__os_syscall_end, -+ -+ -+}; ---- /dev/null -+++ b/arch/ubicom32/kernel/process.c -@@ -0,0 +1,634 @@ -+/* -+ * 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 -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+/* -+ * This file handles the architecture-dependent parts of process handling.. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DUMP_RANGE_REGISTER(REG, IDX) asm volatile ( \ -+ " move.4 %0, "REG"_RANGE"IDX"_EN \n\t" \ -+ " move.4 %1, "REG"_RANGE"IDX"_LO \n\t" \ -+ " move.4 %2, "REG"_RANGE"IDX"_HI \n\t" \ -+ : "=d"(en), "=d"(lo), "=d"(hi) \ -+ ); \ -+ printk(KERN_NOTICE REG"Range"IDX": en:%08x, range: %08x-%08x\n", \ -+ (unsigned int)en, \ -+ (unsigned int)lo, \ -+ (unsigned int)hi) -+ -+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(); -+ } -+} -+ -+/* -+ * address_is_valid() -+ * check if an address is valid -- (for read access) -+ */ -+static bool address_is_valid(const void *address) -+{ -+ int addr = (int)address; -+ unsigned long socm, eocm, sdram, edram; -+ -+ if (addr & 3) -+ return false; -+ -+ processor_ocm(&socm, &eocm); -+ processor_dram(&sdram, &edram); -+ if (addr >= socm && addr < eocm) -+ return true; -+ -+ if (addr >= sdram && addr < edram) -+ return true; -+ -+ return false; -+} -+ -+/* -+ * vma_path_name_is_valid() -+ * check if path_name of a vma is a valid string -+ */ -+static bool vma_path_name_is_valid(const char *str) -+{ -+#define MAX_NAME_LEN 256 -+ int i = 0; -+ if (!address_is_valid(str)) -+ return false; -+ -+ for (; i < MAX_NAME_LEN; i++, str++) { -+ if (*str == '\0') -+ return true; -+ } -+ -+ return false; -+} -+ -+/* -+ * show_vmas() -+ * show vma info of a process -+ */ -+void show_vmas(struct task_struct *task) -+{ -+#ifdef CONFIG_DEBUG_VERBOSE -+#define UBICOM32_MAX_VMA_COUNT 1024 -+ -+ struct vm_area_struct *vma; -+ struct file *file; -+ char *name = ""; -+ int flags, loop = 0; -+ -+ printk(KERN_NOTICE "Start of vma list\n"); -+ -+ if (!address_is_valid(task) || !address_is_valid(task->mm)) -+ goto error; -+ -+ vma = task->mm->mmap; -+ while (vma) { -+ if (!address_is_valid(vma)) -+ goto error; -+ -+ flags = vma->vm_flags; -+ file = vma->vm_file; -+ -+ if (file) { -+ /* seems better to use dentry op here, but sanity check is easier this way */ -+ if (!address_is_valid(file) || !address_is_valid(file->f_path.dentry) || !vma_path_name_is_valid(file->f_path.dentry->d_name.name)) -+ goto error; -+ -+ name = (char *)file->f_path.dentry->d_name.name; -+ } -+ -+ /* Similar to /proc/pid/maps format */ -+ printk(KERN_NOTICE "%08lx-%08lx %c%c%c%c %08lx %s\n", -+ vma->vm_start, -+ vma->vm_end, -+ flags & VM_READ ? 'r' : '-', -+ flags & VM_WRITE ? 'w' : '-', -+ flags & VM_EXEC ? 'x' : '-', -+ flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p', -+ vma->vm_pgoff << PAGE_SHIFT, -+ name); -+ -+ vma = vma->vm_next; -+ -+ if (loop++ > UBICOM32_MAX_VMA_COUNT) -+ goto error; -+ } -+ -+ printk(KERN_NOTICE "End of vma list\n"); -+ return; -+ -+error: -+ printk(KERN_NOTICE "\nCorrupted vma list, abort!\n"); -+#endif -+} -+ -+/* -+ * show_regs() -+ * Print out all of the registers. -+ */ -+void show_regs(struct pt_regs *regs) -+{ -+ unsigned int i; -+ unsigned int en, lo, hi; -+ -+ 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); -+ -+ /* -+ * Dump range registers -+ */ -+ DUMP_RANGE_REGISTER("I", "0"); -+ DUMP_RANGE_REGISTER("I", "1"); -+ DUMP_RANGE_REGISTER("I", "2"); -+ DUMP_RANGE_REGISTER("I", "3"); -+ DUMP_RANGE_REGISTER("D", "0"); -+ DUMP_RANGE_REGISTER("D", "1"); -+ DUMP_RANGE_REGISTER("D", "2"); -+ DUMP_RANGE_REGISTER("D", "3"); -+ DUMP_RANGE_REGISTER("D", "4"); -+ -+ 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(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; -+} -+ -+ -+/* -+ * Infrequently used interface to dump task registers to core files. -+ */ -+int dump_task_regs(struct task_struct *task, elf_gregset_t *elfregs) -+{ -+ struct pt_regs *regs = task_pt_regs(task); -+ *(struct pt_regs *)elfregs = *regs; -+ -+ return 1; -+} -+ -+/* -+ * __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,348 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+struct procnode { -+ struct devtree_node dn; -+ unsigned int threads; -+ unsigned int timers; -+ unsigned int frequency; -+ unsigned int ddr_frequency; -+ unsigned int interrupt0; -+ unsigned int interrupt1; -+ void *socm; -+ void *eocm; -+ void *sdram; -+ void *edram; -+ unsigned int arch_version; -+ void *os_syscall_begin; -+ void *os_syscall_end; -+}; -+ -+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.0 MHz\n", -+ clockfreq / 1000000); -+ seq_printf(m, "DDR Freq:\t%u.0 MHz\n", -+ pn ? pn->ddr_frequency / 1000000 : 0); -+ 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 start and end of OCM available to Linux. -+ */ -+void processor_ocm(unsigned long *socm, unsigned long *eocm) -+{ -+ *socm = (unsigned long)pn->socm; -+ *eocm = (unsigned long)pn->eocm; -+} -+ -+/* -+ * processor_dram() -+ * Returns the start and end of dram available to Linux. -+ */ -+void processor_dram(unsigned long *sdram, unsigned long *edram) -+{ -+ *sdram = (unsigned long)pn->sdram; -+ *edram = (unsigned long)pn->edram; -+} -+ -+/* -+ * processor_validate_failed() -+ * Returns the dram available to Linux. -+ */ -+static noinline void processor_validate_failed(void) -+{ -+ while (1) -+ THREAD_STALL; -+} -+ -+/* -+ * processor_validate() -+ * Validates the procnode against limitations of this link/built. -+ */ -+static void processor_validate(void) -+{ -+ void *dram_start = (void *)(KERNELSTART); -+ void *dram_end = (void *)(SDRAMSTART + CONFIG_MIN_RAMSIZE); -+#if APP_OCM_CODE_SIZE || APP_OCM_DATA_SIZE -+ void *ocm_code_start = (void *)(OCMSTART + APP_OCM_CODE_SIZE); -+ void *ocm_data_end = (void *)(OCMEND - APP_OCM_DATA_SIZE); -+#endif -+ extern void __os_syscall_begin; -+ extern void __os_syscall_end; -+ int proc_node_valid = 1; -+ -+ if (!pn) { -+ printk(KERN_ERR "ERROR: processor node not found\n"); -+ goto error; -+ } -+ -+ -+ if (dram_start < pn->sdram || dram_end > pn->edram) { -+ printk(KERN_ERR "ERROR: processor dram mismatch %p-%p " -+ "available but we are expecting %p-%p\n", -+ pn->sdram, pn->edram, dram_start, dram_end); -+ proc_node_valid = 0; -+ } else { -+ printk(KERN_ERR "processor dram %p-%p, expecting %p-%p\n", -+ pn->sdram, pn->edram, dram_start, dram_end); -+ } -+ if (&__os_syscall_begin < pn->os_syscall_begin || -+ &__os_syscall_end > pn->os_syscall_end) { -+ printk(KERN_ERR "ERROR: processor syscall area mismatch " -+ "%p-%p available but we are expecting %p-%p\n", -+ pn->os_syscall_begin, pn->os_syscall_end, -+ &__os_syscall_begin, &__os_syscall_end); -+ proc_node_valid = 0; -+ } else { -+ printk(KERN_ERR "processor dram %p-%p, expecting %p-%p\n", -+ pn->sdram, pn->edram, dram_start, dram_end); -+ } -+#if APP_OCM_CODE_SIZE || APP_OCM_DATA_SIZE -+ if (ocm_code_start < pn->socm || ocm_data_end > pn->eocm) { -+ printk(KERN_ERR "ERROR: processor ocm mismatch %p-%p " -+ "available but we are expecting %p-%p\n", -+ pn->socm, pn->eocm, ocm_code_start, ocm_data_end); -+ proc_node_valid = 0; -+ } else { -+ printk(KERN_INFO "processor ocm %p-%p, expecting %p-%p\n", -+ pn->socm, pn->eocm, ocm_code_start, ocm_data_end); -+ -+ } -+#endif -+ -+ if (UBICOM32_ARCH_VERSION != pn->arch_version) { -+ printk(KERN_ERR "ERROR: processor arch mismatch, kernel" -+ "compiled for %d found %d\n", -+ UBICOM32_ARCH_VERSION, pn->arch_version); -+ proc_node_valid = 0; -+ } -+ -+ if (proc_node_valid) -+ return; -+error: -+ processor_validate_failed(); -+} -+ -+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"); -+ -+ processor_validate(); -+ -+ /* -+ * If necessary correct the initial range registers to cover the -+ * complete physical space -+ */ -+ if (pn->edram > (void *)(SDRAMSTART + CONFIG_MIN_RAMSIZE)) { -+ printk(KERN_INFO "updating range registers for expanded dram\n"); -+ asm volatile ( -+ " move.4 D_RANGE1_HI, %0 \t\n" -+ " move.4 I_RANGE0_HI, %0 \t\n" -+#ifdef CONFIG_PROTECT_KERNEL -+ " move.4 D_RANGE2_HI, %0 \t\n" -+ " move.4 I_RANGE2_HI, %0 \t\n" -+#endif -+ : : "a"((unsigned long)pn->edram - 4) -+ ); -+ } -+ -+} ---- /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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+/* -+ * Generic semaphore code. Buyer beware. Do your own -+ * specific changes in -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#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 -+ * 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,194 @@ -+/* -+ * 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 -+ * Copyleft ()) 2000 James D. Schettine {james@telos-systems.com} -+ * Copyright (C) 1998 Kenneth Albanowski -+ * Copyright (C) 1995 Hamish Macdonald -+ * Copyright (C) 2000 Lineo Inc. (www.lineo.com) -+ * Copyright (C) 2001 Lineo, Inc. -+ * 68VZ328 Fixes/support Evan Stawnyczy -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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; -+ unsigned long ram_start; -+ -+ processor_init(); -+ bootargs_init(); -+ -+ /* -+ * Use the link for memory_start from the link and the processor -+ * node for memory_end. -+ */ -+ memory_start = PAGE_ALIGN(((unsigned long)&_end)); -+ processor_dram(&ram_start, &memory_end); -+ -+ 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; -+ -+ /* -+ * bootexec copies the original default command line to end of memory. -+ * u-boot can modify it there (i.e. to enable network boot) and the -+ * kernel picks up the modified version. -+ * -+ * mainexec creates a `new default' command_line which is in the -+ * bootargs devnode. It is updated on every firmware update but -+ * not used at the moment. -+ */ -+ strlcpy(boot_command_line, (char *)(memory_end - COMMAND_LINE_SIZE), 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. \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 -+ -+#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 . -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#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,806 @@ -+/* -+ * arch/ubicom32/kernel/smp.c -+ * SMP implementation for Ubicom32 processors. -+ * -+ * (C) Copyright 2009, Ubicom, Inc. -+ * Copyright (C) 1999 Walt Drummond -+ * Copyright (C) 1999 David Mosberger-Tang -+ * Copyright (C) 2001,2004 Grant Grundler -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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, -+}; -+ -+/* -+ * We maintain a hardware thread oriented view of online threads -+ * and those involved or needing IPI. -+ */ -+static volatile unsigned long smp_online_threads = 0; -+static volatile unsigned long smp_needs_ipi = 0; -+static volatile 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 &= ~(1 << 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); -+ smp_debug(100, KERN_INFO "cpu[%d]: send: %d\n", cpu, op); -+} -+ -+/* -+ * ipi_send_mask -+ * Send an IPI to each cpu in mask. -+ */ -+static inline void ipi_send_mask(unsigned int op, const struct cpumask 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(); -+ struct cpumask result; -+ cpumask_copy(&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); -+ smp_debug(100, KERN_INFO "cpu[%d]: unamesk vector\n", smp_processor_id()); -+} -+ -+/* -+ * 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_debug(100, KERN_INFO "smp: reset IPIs for: 0x%x\n", mask); -+} -+ -+/* -+ * 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, const struct cpumask *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(const struct cpumask *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_mask(const struct cpumask *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); -+} -+ -+/* -+ * setup_profiling_timer() -+ * Dummy function created to keep Oprofile happy in the SMP case. -+ */ -+int setup_profiling_timer(unsigned int multiplier) -+{ -+ return 0; -+} -+ -+/* -+ * 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); -+ int i; -+ for_each_cpu_mask(i, CPU_MASK_ALL) -+ set_cpu_possible(i, true); -+ -+ current_thread_info()->cpu = 0; -+ p->tid = thread_get_self(); -+} ---- /dev/null -+++ b/arch/ubicom32/kernel/stacktrace.c -@@ -0,0 +1,244 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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,376 @@ -+/* -+ * arch/ubicom32/kernel/syscalltable.S -+ * -+ * -+ * (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 . -+ * -+ * 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 , Kenneth Albanowski , -+ * Copyright (C) 2000 Lineo Inc. (www.lineo.com) -+ * Copyright (C) 1991, 1992 Linus Torvalds -+ */ -+ -+#include -+#include -+#include -+ -+.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 . -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+/* 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+ -+/* -+ * 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 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ * arch/arm -+ * arch/sh -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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,301 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if defined(CONFIG_SMP) -+#include -+#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, const 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; -+ cpumask_copy(&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)); -+} -+#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_PTR); -+ -+#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_PTR); -+#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_PTR); -+ } -+} ---- /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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+ -+#include -+#include -+#if defined(CONFIG_SMP) -+#include -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+ -+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,514 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+/* -+ * Sets up all exception vectors -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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; -+ -+extern void show_vmas(struct task_struct *task); -+ -+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 code_end; -+ 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; -+ -+ processor_dram(&code_start, &code_end); -+ -+ /* -+ * 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"); -+ show_vmas(task); -+ } -+ 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, KERNELSTART, memory_end) && -+ (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+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,359 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * begin_restore_context() -+ * Restore most of the context from sp (struct pt_reg *) -+ * -+ * This *can* be called without the global atomic lock. (because sp is -+ * not restored!) Only d15 and a3 are allowed to be used after this -+ * before calling complete_restore_context -+ */ -+.macro begin_restore_context -+ move.4 d0, PT_D0(sp) -+ move.4 d1, PT_D1(sp) -+ move.4 d2, PT_D2(sp) -+ move.4 d3, PT_D3(sp) -+ move.4 d4, PT_D4(sp) -+ move.4 d5, PT_D5(sp) -+ move.4 d6, PT_D6(sp) -+ move.4 d7, PT_D7(sp) -+ move.4 d8, PT_D8(sp) -+ move.4 d9, PT_D9(sp) -+ move.4 d10, PT_D10(sp) -+ move.4 d11, PT_D11(sp) -+ move.4 d12, PT_D12(sp) -+ move.4 d13, PT_D13(sp) -+ move.4 d14, PT_D14(sp) -+;; move.4 d15, PT_D15(sp) -+ move.4 a0, PT_A0(sp) -+ move.4 a1, PT_A1(sp) -+ move.4 a2, PT_A2(sp) -+;; move.4 a3, PT_A3(sp) -+ move.4 a4, PT_A4(sp) -+ move.4 a5, PT_A5(sp) -+ move.4 a6, PT_A6(sp) -+ move.4 acc0_hi, PT_ACC0HI(sp) -+ move.4 acc0_lo, PT_ACC0LO(sp) -+ move.4 mac_rc16, PT_MAC_RC16(sp) -+ move.4 acc1_hi, PT_ACC1HI(sp) -+ move.4 acc1_lo, PT_ACC1LO(sp) -+ move.4 source3, PT_SOURCE3(sp) -+ move.4 int_mask0, PT_INT_MASK0(sp) -+ move.4 int_mask1, PT_INT_MASK1(sp) -+.endm -+ -+/* -+ * complete_restore_context() -+ * Completely restore the 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 complete_restore_context -+ move.4 a3, sp -+ move.4 d15, PT_D15(sp) -+ move.4 sp, PT_SP(a3) ; Recover Stack pointer from save area -+ move.4 -4(sp)++, PT_PC(a3) ; Recover saved PC and save to stack -+ move.4 -4(sp)++, PT_CSR(a3) ; Recover saved csr and save to stack -+ move.4 a3, PT_A3(a3) -+.endm -+ -+/* -+ * old restore_context macro -+ */ -+.macro restore_context -+ begin_restore_context -+ complete_restore_context -+.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, "ax", @progbits -+ .global ret_from_interrupt_to_kernel -+ret_from_interrupt_to_kernel: -+ begin_restore_context ; Restore the thread context -+ atomic_lock_acquire ; Enter critical section -+ complete_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 critical section handling in this code? -+ * -+ */ -+ .section .text.ret_from_interrupt_to_user, "ax", @progbits -+ .global ret_from_interrupt_to_user -+ret_from_interrupt_to_user: -+ ldsr_thread_enable_interrupts ; enable the threads interrupts -+ /* -+ * Set a1 to the thread info pointer, no need to save it as we are -+ * restoring userspace and will never return -+ */ -+ movei d0, #(~(ASM_THREAD_SIZE-1)) -+ and.4 a1, sp, d0 -+ -+ /* -+ * Test if the scheduler needs to be called. -+ */ -+ btst TI_FLAGS(a1), #ASM_TIF_NEED_RESCHED -+ jmpeq.t 2f -+ call a5, schedule ; Call the scheduler. I will come back here. -+ -+ /* -+ * See if we have pending signals and call do_signal -+ * if needed. -+ */ -+2: -+ btst TI_FLAGS(a1), #ASM_TIF_SIGPENDING ; Any signals needed? -+ jmpeq.t 1f -+ -+ /* -+ * Now call do_signal() -+ */ -+ move.4 d0, #0 ; oldset pointer is NULL -+ move.4 d1, sp ; d1 is the regs pointer -+ call a5, do_signal ; Call do_signal() -+ -+ /* -+ * Back from do_signal(), re-enter critical section. -+ */ -+1: -+ begin_restore_context ; Restore the thread context -+ atomic_lock_acquire ; Enter critical section -+ call a3, __complete_and_return_to_userspace ; jump to unprotected section -+ -+/* -+ * 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. -+ */ -+ .section .text.restore_all_registers, "ax", @progbits -+ .global restore_all_registers -+restore_all_registers: -+ begin_restore_context ; Restore the thread context -+ atomic_lock_acquire ; Enter critical section -+ call a3, __complete_and_return_to_userspace -+ -+/* -+ * __complete_and_return_to_userspace -+ * -+ * restores the second half of the context and returns -+ * You must have the atomic lock when you call this function -+ */ -+ .section .kernel_unprotected, "ax", @progbits -+__complete_and_return_to_userspace: -+ disable_kernel_ranges_for_current d15 ; disable kernel ranges -+ complete_restore_context ; restore previous context -+ atomic_lock_release ; Leave critical section -+ 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 .text.ret_from_fork, "ax", @progbits -+ .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) -+ call a5, schedule_tail -+ -+ 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 -+ ;; I think atomic_lock_acquire could be moved here.. -+ 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. -+ -+#ifdef CONFIG_PROTECT_KERNEL -+ call a3, __ret_from_fork_bottom_half -+ .section .kernel_unprotected, "ax", @progbits -+__ret_from_fork_bottom_half: -+ disable_kernel_ranges_for_current d15 -+#endif -+ 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) -+ */ -+ .section .text.__switch_to, "ax", @progbits -+ .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 - bit's 6 through 31 are zeroes anyway. -+ move.4 (a4, d15), a3 ; Load the thread info pointer into the hw_ksp array.. -+ -+ /* -+ * We are done with context switch. Time to return.. -+ */ -+ calli a5, 0(a5) -+ .size __switch_to, . - __switch_to -+ -+/* -+ * 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); -+ */ -+ .section .text.ubicom32_emulate_insn, "ax", @progbits -+ .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 -+ .size ubicom32_emulate_insn, . - ubicom32_emulate_insn ---- /dev/null -+++ b/arch/ubicom32/kernel/ubicom32_ksyms.c -@@ -0,0 +1,98 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* 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); -+EXPORT_SYMBOL(memmove); -+ -+#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 __divdi3(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(__divdi3); -+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,694 @@ -+/* -+ * arch/ubicom32/kernel/ubicom32_syscall.S -+ * -+ * -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+/* -+ * __old_system_call() -+ */ -+ .section .old_syscall_entry.text, "ax", @progbits -+#ifdef CONFIG_OLD_40400010_SYSTEM_CALL -+__old_system_call: -+ call a3, system_call -+ .size __old_system_call, . - __old_system_call ; -+#else -+ /* -+ * something that will crash the userspace application, but -+ * should not take down the kernel, if protection is enabled -+ * this will never even get executed. -+ */ -+ .long 0xFABBCCDE ; illegal instruction -+ bkpt #-1 ; we will never get here -+#endif -+ -+/* -+ * system_call() -+ */ -+ .section .syscall_entry.text, "ax", @progbits -+ .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 -+ /* -+ * in order to reduce the size of code in the syscall section we get -+ * out of it right now -+ */ -+ call a4, __system_call_bottom_half -+ .size system_call, . - system_call -+ -+ .section .text.__system_call_bottom_half, "ax", @progbits -+__system_call_bottom_half: -+ -+ /* -+ * 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 -+ 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) -+ */ -+ call a5, syscall_trace -+ -+ /* -+ * 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_bottom_half, . - __system_call_bottom_half -+ -+/* -+ * syscall_exit() -+ */ -+ .section .text.syscall_exit -+ .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 -+ -+ call a4, __syscall_exit_bottom_half -+ -+ .section .kernel_unprotected, "ax", @progbits -+__syscall_exit_bottom_half: -+ /* -+ * 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. -+ */ -+ .section .text.syscall_exit, "ax", @progbits -+.Lsyscall_exit__post_processing: -+ btst TI_FLAGS(a1), #ASM_TIF_SYSCALL_TRACE -+ jmpeq.t 1f -+ call a5, syscall_trace -+ -+ /* -+ * 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 -+ call a5, schedule -+ -+ /* -+ * 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. -+ call a5, do_signal -+ -+ 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, "ax", @progbits -+ .global kernel_execve -+kernel_execve: -+ move.4 -4(sp)++, a5 ; Save return address -+ /* -+ * Call execve -+ */ -+ movei d8, #__NR_execve ; call execve -+ call a5, system_call -+ 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. -+ */ -+ call a3, __enable_kernel_ranges ; and jump back to kernel -+#else -+ ret a5 ; jump back to the kernel -+#endif -+ -+ .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, "ax", @progbits -+ .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 -+ call a5, system_call ; as we are 'in' the kernel -+ ; we can call kernel_syscall -+ -+ 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, "ax", @progbits -+ .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. -+ */ -+ call a5, __enable_kernel_ranges -+#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 -+ -+#ifdef CONFIG_PROTECT_KERNEL -+ .section .kernel_unprotected, "ax", @progbits -+__enable_kernel_ranges: -+ atomic_lock_acquire ; Enter critical section -+ enable_kernel_ranges_for_current d15 -+ atomic_lock_release ; Leave critical section -+ calli a5, 0(a5) -+ .size __enable_kernel_ranges, . - __enable_kernel_ranges -+ -+#endif -+ -+/* -+ * The following system call intercept functions where we setup the -+ * input to the real system call. In all cases these are just taking -+ * the current sp which is pointing to pt_regs and pushing it into the -+ * last arg of the system call. -+ * -+ * i.e. the public definition of sys_execv is -+ * sys_execve( char *name, -+ * char **argv, -+ * char **envp ) -+ * but process.c defines it as -+ * sys_execve( char *name, -+ * char **argv, -+ * char **envp, -+ * struct pt_regs *regs ) -+ * -+ * so execve_intercept needs to populate the 4th arg with pt_regs*, -+ * which is the stack pointer as we know we must be coming out of -+ * system_call -+ * -+ * The intercept vectors are referenced by syscalltable.S -+ */ -+ -+/* -+ * execve_intercept() -+ */ -+ .section .text.execve_intercept, "ax", @progbits -+ .global execve_intercept -+execve_intercept: -+ move.4 d3, sp ; Save pt_regs address -+ call a3, sys_execve -+ -+ .size execve_intercept, . - execve_intercept -+ -+/* -+ * vfork_intercept() -+ */ -+ .section .text.vfork_intercept, "ax", @progbits -+ .global vfork_intercept -+vfork_intercept: -+ move.4 d0, sp ; Save pt_regs address -+ call a3, sys_vfork -+ -+ .size vfork_intercept, . - vfork_intercept -+ -+/* -+ * clone_intercept() -+ */ -+ .section .text.clone_intercept, "ax", @progbits -+ .global clone_intercept -+clone_intercept: -+ move.4 d2, sp ; Save pt_regs address -+ call a3, sys_clone -+ -+ .size clone_intercept, . - clone_intercept -+ -+/* -+ * sys_sigsuspend() -+ */ -+ .section .text.sigclone_intercept, "ax", @progbits -+ .global sys_sigsuspend -+sys_sigsuspend: -+ move.4 d0, sp ; Pass pointer to pt_regs in d0 -+ call a3, do_sigsuspend -+ -+ .size sys_sigsuspend, . - sys_sigsuspend -+ -+/* -+ * sys_rt_sigsuspend() -+ */ -+ .section .text.sys_rt_sigsuspend, "ax", @progbits -+ .global sys_rt_sigsuspend -+sys_rt_sigsuspend: -+ move.4 d0, sp ; Pass pointer to pt_regs in d0 -+ call a3, do_rt_sigsuspend -+ -+ .size sys_rt_sigsuspend, . - sys_rt_sigsuspend -+ -+/* -+ * sys_rt_sigreturn() -+ */ -+ .section .text.sys_rt_sigreturn, "ax", @progbits -+ .global sys_rt_sigreturn -+sys_rt_sigreturn: -+ move.4 d0, sp ; Pass pointer to pt_regs in d0 -+ call a3, do_rt_sigreturn -+ -+ .size sys_rt_sigreturn, . - sys_rt_sigreturn -+ -+/* -+ * sys_sigaltstack() -+ */ -+ .section .text.sys_sigaltstack, "ax", @progbits -+ .global sys_sigaltstack -+sys_sigaltstack: -+ move.4 d0, sp ; Pass pointer to pt_regs in d0 -+ call a3, do_sys_sigaltstack -+ -+ .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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#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,370 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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 -+ -+/* -+ * 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 KERNELSTART -+#define RAM_LENGTH ((SDRAMSTART + CONFIG_MIN_RAMSIZE) - RAM_START) -+#define TEXT ram -+#define DATA ram -+#define INIT ram -+#define BSS ram -+ -+#ifndef DATA_ADDR -+#define DATA_ADDR -+#endif -+ -+#include -+ -+OUTPUT_ARCH(ubicom32) -+ENTRY(_start) -+ -+MEMORY { -+ ram : ORIGIN = RAM_START, LENGTH = RAM_LENGTH -+ syscall : ORIGIN = OS_SYSCALL_BEGIN, LENGTH = (OS_SYSCALL_END - OS_SYSCALL_BEGIN) -+ 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 = SDRAMSTART; -+__sdram_limit = __sdram_begin + CONFIG_MIN_RAMSIZE; -+__filemedia_begin_addr = FLASHSTART; -+__filemedia_end_addr = __filemedia_begin_addr + 0x00800000; -+ -+/* -+ * For internal diagnostics -+ */ -+__os_syscall_begin = OS_SYSCALL_BEGIN; -+__os_syscall_end = OS_SYSCALL_END; -+ -+SECTIONS { -+ -+ .fixed_text : { -+ _begin = .; -+ *(.skip_syscall) -+ *(.old_syscall_entry.text) -+ __fixed_text_end = .; -+ } > TEXT -+ . = _begin + SIZEOF(.fixed_text) ; -+ -+ /* -+ * System call text in lower ocm (fixed location, can never change) -+ */ -+ __syscall_text_load_begin = .; -+ __syscall_text_run_begin = OS_SYSCALL_BEGIN; -+ -+ .syscall_text __syscall_text_run_begin : AT(__syscall_text_load_begin) { -+ *(.syscall_entry.text) /* Must be at OS_SYSCALL_BEGIN 0x3ffc0040 */ -+ *(.kernel_unprotected) -+ . = ALIGN(4); -+ __syscall_text_run_end = .; -+ } > syscall /* .syscall_text */ -+ . = __syscall_text_load_begin + __syscall_text_run_end - __syscall_text_run_begin ; -+ __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 -+ . = 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 = .; -+ -+ *(.rodata) *(.rodata.*) -+ *(__vermagic) /* Kernel version magic */ -+ *(__markers_strings) -+ *(.rodata1) -+ *(.rodata.str1.1) -+ *(__tracepoints_strings) -+ -+ /* PCI quirks */ -+ __start_pci_fixups_early = . ; -+ *(.pci_fixup_early) -+ __end_pci_fixups_early = . ; -+ __start_pci_fixups_header = . ; -+ *(.pci_fixup_header) -+ __end_pci_fixups_header = . ; -+ __start_pci_fixups_final = . ; -+ *(.pci_fixup_final) -+ __end_pci_fixups_final = . ; -+ __start_pci_fixups_enable = . ; -+ *(.pci_fixup_enable) -+ __end_pci_fixups_enable = . ; -+ __start_pci_fixups_resume = . ; -+ *(.pci_fixup_resume) -+ __end_pci_fixups_resume = . ; -+ __start_pci_fixups_resume_early = . ; -+ *(.pci_fixup_resume_early) -+ __end_pci_fixups_resume_early = . ; -+ __start_pci_fixups_suspend = . ; -+ *(.pci_fixup_suspend) -+ __end_pci_fixups_suspend = . ; -+ -+ __start_builtin_fw = . ; -+ *(.builtin_fw) -+ __end_builtin_fw = . ; -+ -+ -+ /* Kernel symbol table: Normal symbols */ -+ . = ALIGN(4); -+ __start___ksymtab = .; -+ *(__ksymtab) -+ __stop___ksymtab = .; -+ -+ /* Kernel symbol table: GPL-only symbols */ -+ __start___ksymtab_gpl = .; -+ *(__ksymtab_gpl) -+ __stop___ksymtab_gpl = .; -+ -+ /* Kernel symbol table: Normal unused symbols */ -+ __start___ksymtab_unused = .; -+ *(__ksymtab_unused) -+ __stop___ksymtab_unused = .; -+ -+ /* Kernel symbol table: GPL-only unused symbols */ -+ __start___ksymtab_unused_gpl = .; -+ *(__ksymtab_unused_gpl) -+ __stop___ksymtab_unused_gpl = .; -+ -+ /* Kernel symbol table: GPL-future symbols */ -+ __start___ksymtab_gpl_future = .; -+ *(__ksymtab_gpl_future) -+ __stop___ksymtab_gpl_future = .; -+ -+ /* Kernel symbol table: Normal symbols */ -+ __start___kcrctab = .; -+ *(__kcrctab) -+ __stop___kcrctab = .; -+ -+ /* Kernel symbol table: GPL-only symbols */ -+ __start___kcrctab_gpl = .; -+ *(__kcrctab_gpl) -+ __stop___kcrctab_gpl = .; -+ -+ /* Kernel symbol table: GPL-future symbols */ -+ __start___kcrctab_gpl_future = .; -+ *(__kcrctab_gpl_future) -+ __stop___kcrctab_gpl_future = .; -+ -+ /* Kernel symbol table: strings */ -+ *(__ksymtab_strings) -+ -+ /* Built-in module parameters */ -+ . = ALIGN(4) ; -+ __start___param = .; -+ *(__param) -+ __stop___param = .; -+ -+ . = ALIGN(4) ; -+ _etext = . ; -+ } > TEXT -+ -+ .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 -+ -+ /DISCARD/ : { -+ EXIT_TEXT -+ EXIT_DATA -+ *(.exitcall.exit) -+ } -+ -+ .bss : { -+ . = ALIGN(4); -+ _sbss = . ; -+ *(.bss) -+ *(COMMON) -+ . = ALIGN(4) ; -+ _ebss = . ; -+ _end = . ; -+ } > BSS -+ -+ 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 . -+ * -+ * 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, -+ * Arnt Gulbrandsen, -+ * Tom May, -+ * Andreas Schwab, -+ * 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 -+#include -+ -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+ -+/* -+ * 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 -+# -+# -+# (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 . -+# -+# 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 ---- /dev/null -+++ b/arch/ubicom32/lib/mem_ubicom32.c -@@ -0,0 +1,343 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+ -+#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; -+} -+ -+void *memmove(void *dest, const void *src, size_t n) -+{ -+ char *tmp; -+ const char *s; -+ -+ if (n == 0) -+ return dest; -+ -+ tmp = dest; -+ s = src; -+ -+ /* -+ * Will perform 16-bit move if possible -+ */ -+ if (likely((((u32)dest | (u32)src | n) & 1) == 0)) { -+ if (dest <= src) { -+ asm volatile ( -+ " sub.4 d15, #0, %2 \n\t" // set up for jump table -+ " and.4 d15, #(32-2), d15 \n\t" // d15 = (- count) & (32 - 2) -+ " moveai a3, #%%hi(1f) \n\t" -+ " lea.1 a3, %%lo(1f)(a3) \n\t" -+ " lea.2 a3, (a3,d15) \n\t" -+ " calli a3, 0(a3) \n\t" -+ -+ "1: .rept 16 \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" -+ -+ : "+a" (tmp), "+a" (s), "+d" (n) -+ : -+ : "d15", "a3", "memory", "cc" -+ ); -+ } else { -+ tmp += n; -+ s += n; -+ asm volatile ( -+ " sub.4 d15, #0, %2 \n\t" // set up for jump table -+ " and.4 d15, #(32-2), d15 \n\t" // d15 = (- count) & (32 - 2) -+ " moveai a3, #%%hi(1f) \n\t" -+ " lea.1 a3, %%lo(1f)(a3) \n\t" -+ " lea.2 a3, (a3,d15) \n\t" -+ " calli a3, 0(a3) \n\t" -+ -+ "1: .rept 16 \n\t" -+ " move.2 -2(%0)++, -2(%1)++ \n\t" -+ " .endr \n\t" -+ " add.4 %2, #-32, %2 \n\t" -+ " jmpgt.w.f 1b \n\t" -+ -+ : "+a" (tmp), "+a" (s), "+d" (n) -+ : -+ : "d15", "a3", "memory", "cc" -+ ); -+ } -+ return dest; -+ } -+ -+ if (dest <= src) { -+ asm volatile ( -+ " sub.4 d15, #0, %2 \n\t" // set up for jump table -+ " and.4 d15, #(16-1), d15 \n\t" // d15 = (- count) & (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" -+ : "+a" (tmp), "+a" (s), "+d" (n) -+ : -+ : "d15", "a3", "memory", "cc" -+ ); -+ } else { -+ tmp += n; -+ s += n; -+ asm volatile ( -+ " sub.4 d15, #0, %2 \n\t" // set up for jump table -+ " and.4 d15, #(16-1), d15 \n\t" // d15 = (- count) & (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 -1(%0)++, -1(%1)++ \n\t" -+ " .endr \n\t" -+ " add.4 %2, #-16, %2 \n\t" -+ " jmpgt.w.f 1b \n\t" -+ : "+a" (tmp), "+a" (s), "+d" (n) -+ : -+ : "d15", "a3", "memory", "cc" -+ ); -+ } -+ return dest; -+} ---- /dev/null -+++ b/arch/ubicom32/mach-common/audio.c -@@ -0,0 +1,134 @@ -+/* -+ * arch/ubicom32/mach-common/audio.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 . -+ */ -+ -+#include -+#include -+ -+#include -+#include -+#include -+ -+/* -+ * The number of audio devices currently allocated, used for .id -+ */ -+static int __initdata audio_device_count; -+ -+/* -+ * The maximum number of resources (cards) that the audio will have. -+ * Currently 3, a register space, and up to 2 interrupts. -+ */ -+#define AUDIO_MAX_RESOURCES 3 -+ -+/* -+ * audio_device_alloc -+ * Checks the device tree and allocates a platform_device if found -+ */ -+struct platform_device * __init audio_device_alloc(const char *driver_name, -+ const char *node_name, const char *inst_name, int priv_bytes) -+{ -+ struct platform_device *pdev; -+ struct resource *res; -+ struct audio_node *audio_node; -+ struct ubi32pcm_platform_data *pdata; -+ struct audio_dev_regs *adr; -+ int idx; -+ -+ /* -+ * Check the device tree for the audio node -+ */ -+ audio_node = (struct audio_node *)devtree_find_node(node_name); -+ if (!audio_node) { -+ printk(KERN_WARNING "audio device '%s' not found\n", node_name); -+ return NULL; -+ } -+ -+ if (audio_node->version != AUDIONODE_VERSION) { -+ printk(KERN_WARNING "audio node not compatible\n"); -+ return NULL; -+ } -+ -+ /* -+ * Find the instance in this node -+ */ -+ adr = audio_node->regs->adr; -+ for (idx = 0; idx < audio_node->regs->max_devs; idx++) { -+ if ((adr->version == AUDIO_DEV_REGS_VERSION) && -+ (strcmp(adr->name, inst_name) == 0)) { -+ break; -+ } -+ adr++; -+ } -+ if (idx == audio_node->regs->max_devs) { -+ printk(KERN_WARNING "audio inst '%s' not found in device '%s'\n", inst_name, node_name); -+ 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 could not alloc pdev\n"); -+ return NULL; -+ } -+ -+ res = kzalloc(sizeof(struct resource) * AUDIO_MAX_RESOURCES, -+ GFP_KERNEL); -+ if (!res) { -+ kfree(pdev); -+ printk(KERN_WARNING "audio could not alloc res\n"); -+ return NULL; -+ } -+ -+ pdev->name = driver_name; -+ pdev->id = audio_device_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 + AUDIO_TX_IRQ_RESOURCE].start = audio_node->dn.sendirq; -+ res[1 + AUDIO_TX_IRQ_RESOURCE].flags = IORESOURCE_IRQ; -+ res[1 + AUDIO_RX_IRQ_RESOURCE].start = audio_node->dn.recvirq; -+ res[1 + AUDIO_RX_IRQ_RESOURCE].flags = IORESOURCE_IRQ; -+ pdev->num_resources = 3; -+ -+ printk(KERN_INFO "Audio.%d '%s':'%s' found irq=%d/%d.%d regs=%p pdev=%p/%p\n", -+ pdev->id, node_name, inst_name, audio_node->dn.sendirq, -+ audio_node->dn.recvirq, idx, audio_node->regs, pdev, res); -+ pdata = (struct ubi32pcm_platform_data *)(pdev + 1); -+ pdev->dev.platform_data = pdata; -+ pdata->node_name = node_name; -+ pdata->inst_name = inst_name; -+ pdata->inst_num = idx; -+ 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+ -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+ -+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,136 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+ -+/* -+ * 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; -+ int dccr = cc == DCCR_BASE; -+ if (dccr && 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) -+ ); -+ } -+ -+ if (dccr) -+ UBICOM32_LOCK(DCCR_LOCK_BIT); -+ else -+ UBICOM32_LOCK(ICCR_LOCK_BIT); -+ -+ /* -+ * 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 (dccr && 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); -+ } -+ -+ if (dccr) -+ UBICOM32_UNLOCK(DCCR_LOCK_BIT); -+ else -+ UBICOM32_UNLOCK(ICCR_LOCK_BIT); -+ -+} -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+#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/Kconfig.switch -@@ -0,0 +1,12 @@ -+menuconfig UBICOM_SWITCH -+ tristate "Switch devices" -+ help -+ This option provides Ethernet switch management options via proc fs -+ -+if UBICOM_SWITCH -+config UBICOM_SWITCH_BCM539X -+ tristate "Broadcom BCM539X series (SPI)" -+ depends on SPI_MASTER -+ help -+ Supports Broadcom BCM539X Gigabit Ethernet Switches over SPI -+endif ---- /dev/null -+++ b/arch/ubicom32/mach-common/Makefile -@@ -0,0 +1,41 @@ -+# -+# 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 . -+# -+# 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 profile.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_UBICOM_SWITCH) += switch-core.o -+obj-$(CONFIG_UBICOM_SWITCH_BCM539X) += switch-bcm539x.o -+obj-$(CONFIG_UIO_UBICOM32RING) += ring_tio.o -+obj-$(CONFIG_SND_UBI32) += audio.o -+obj-$(CONFIG_UBICOM32_PLIO) += plio.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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+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/plio.c -@@ -0,0 +1,92 @@ -+/* -+ * plio.c -+ * PLIO state machine support functions -+ * -+ * Copyright © 2009 Ubicom Inc. . 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+ -+/* -+ * plio_reset -+ * Select and reset PLIO function -+ */ -+static void plio_reset(const plio_fctl_t *plio_fctl) { -+ plio_io_function_t plio_function = { -+ .fn_sel = PLIO_FN, -+ .fn_reset = 1, -+ }; -+ -+ /* -+ * enable extension port -+ */ -+ PEXT_NBR->function = plio_function; -+ -+ /* -+ * program clock dividers -+ */ -+ PLIO_NBR->fctl2 = plio_fctl->fctl2; -+ -+ /* -+ * select plio function and assert function reset -+ */ -+ plio_function.br_thread = thread_get_self(); -+ plio_function.fn_reset = 1; -+ PLIO_NBR->function = plio_function; -+ -+ /* -+ * program plio controls -+ */ -+ PLIO_NBR->fctl0 = plio_fctl->fctl0; -+ PLIO_NBR->fctl1 = plio_fctl->fctl1; -+ -+ /* -+ * deassert function reset -+ */ -+ plio_function.fn_reset = 0; -+ PLIO_NBR->function = plio_function; -+} -+ -+/* -+ * plio_init -+ * configure and initialize PLIO. -+ */ -+void plio_init(const plio_fctl_t *plio_fctl, const plio_config_t *plio_config, const plio_sram_t plio_sram_cfg[], int sram_cfg_size){ -+ /* -+ * first reset to start plio clock -+ */ -+ plio_reset(plio_fctl); -+ -+ udelay(1); -+ -+ /* -+ * configure pfsm -+ */ -+ PLIO_NBR->fctl0.pfsm_prog = 1; -+ memcpy(PLIO_BR->pfsm_sram, plio_sram_cfg, sram_cfg_size); -+ PLIO_NBR->fctl0.pfsm_prog = 0; -+ -+ /* -+ * program rest of plio -+ */ -+ memcpy(&PLIO_BR->config, plio_config, sizeof(plio_config_t)); -+} ---- /dev/null -+++ b/arch/ubicom32/mach-common/profile.c -@@ -0,0 +1,549 @@ -+/* -+ * arch/ubicom32/mach-common/profile.c -+ * Implementation for Ubicom32 Profiler -+ * -+ * (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 . -+ */ -+ -+#include -+#include "profile.h" -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * spacs for all memory blocks so we can hold locks for short time when walking tables -+ */ -+#define PROFILE_NUM_MAPS 5000 -+static struct profile_map profile_pm[PROFILE_NUM_MAPS]; -+ -+static struct profilenode *node = NULL; -+static int profile_first_packet = 1; -+ -+static int profile_open(struct inode *inode, struct file *filp) -+{ -+ if (!node) { -+ return -ENOENT; -+ } -+ node->busy = 1; -+ if (!node->enabled) { -+ node->enabled = 1; -+ node->busy = 0; -+ profile_first_packet = 1; -+ return 0; -+ } -+ node->busy = 0; -+ return -EBUSY; -+} -+ -+static int profile_sequence_num; -+ -+/* -+ * make a packet full of sample data -+ */ -+static int profile_make_data_packet(char *buf, int count) -+{ -+ int samples; /* number of samples requested */ -+ int i; -+ struct profile_header ph; -+ char *ptr; -+ -+ if (count < sizeof(struct profile_header) + sizeof(struct profile_sample)) { -+ return -EINVAL; -+ } -+ -+ /* -+ * fill in the packet header -+ */ -+ memset(&ph, 0, sizeof(struct profile_header)); -+ ph.magic = PROF_MAGIC + PROFILE_VERSION; -+ ph.header_size = sizeof(struct profile_header); -+ ph.clocks = node->clocks; -+ for (i = 0; i < PROFILE_MAX_THREADS; ++i) { -+ ph.instruction_count[i] = node->inst_count[i]; -+ } -+ ph.profile_instructions = 0; -+ ph.enabled = node->enabled_threads; -+ ph.hrt = node->hrt; -+ ph.high = 0; -+ ph.profiler_thread = node->profiler_thread; -+ ph.clock_freq = node->clock_freq; -+ ph.seq_num = profile_sequence_num++; -+ ph.cpu_id = node->cpu_id; -+ ph.perf_counters[0] = node->stats[0]; -+ ph.perf_counters[1] = node->stats[1]; -+ ph.perf_counters[2] = node->stats[2]; -+ ph.perf_counters[3] = node->stats[3]; -+ ph.ddr_freq = node->ddr_freq; -+ -+ ptr = buf + sizeof(struct profile_header); -+ -+ samples = (count - sizeof(struct profile_header)) / sizeof(struct profile_sample); -+ for (i = 0; i < samples && node->count; ++i) { -+ if (copy_to_user(ptr, &node->samples[node->tail], sizeof(struct profile_sample)) != 0) { -+ return -EFAULT; -+ } -+ node->count--; -+ node->tail++; -+ if (node->tail >= node->max_samples) { -+ node->tail = 0; -+ } -+ ptr += sizeof(struct profile_sample); -+ } -+ ph.sample_count = i; -+ if (copy_to_user(buf, &ph, sizeof(struct profile_header)) != 0) { -+ return -EFAULT; -+ } -+ if (ph.sample_count == 0) -+ return 0; -+ else -+ return sizeof(struct profile_header) + ph.sample_count * sizeof(struct profile_sample); -+} -+ -+static void profile_get_memory_stats(unsigned int *total_free, unsigned int *max_free) -+{ -+ struct list_head *p; -+ struct zone *zone; -+ unsigned int size; -+ -+ *total_free = 0; -+ *max_free = 0; -+ -+ /* -+ * get all the free regions. In each zone, the array of free_area lists contains the first page of each frame of size 1 << order -+ */ -+ for_each_zone(zone) { -+ unsigned long order, flags, i; -+ -+ if (!populated_zone(zone)) -+ continue; -+ -+ if (!is_normal(zone)) -+ continue; -+ -+ spin_lock_irqsave(&zone->lock, flags); -+ for_each_migratetype_order(order, i) { -+ size = ((1 << order) << PAGE_SHIFT) >> 10; -+ list_for_each(p, &(zone->free_area[order].free_list[i])) { -+ if (size > *max_free) { -+ *max_free = size; -+ } -+ *total_free += size; -+ } -+ } -+ spin_unlock_irqrestore(&zone->lock, flags); -+ } -+} -+ -+struct profile_counter_pkt profile_builtin_stats[] = -+{ -+ { -+ "Free memory(KB)", 0 -+ }, -+ { -+ "Max free Block(KB)", 0 -+ } -+}; -+ -+/* -+ * make a packet full of performance counters -+ */ -+static char prof_pkt[PROFILE_MAX_PACKET_SIZE]; -+static int profile_make_stats_packet(char *buf, int count) -+{ -+ char *ptr = prof_pkt; -+ struct profile_header_counters hdr; -+ int stat_count = 0; -+ int i; -+ unsigned int total_free, max_free; -+ int builtin_count = sizeof(profile_builtin_stats) / sizeof(struct profile_counter_pkt); -+ -+ if (count > PROFILE_MAX_PACKET_SIZE) { -+ count = PROFILE_MAX_PACKET_SIZE; -+ } -+ stat_count = (count - sizeof(struct profile_header_counters)) / sizeof (struct profile_counter_pkt); -+ stat_count -= builtin_count; -+ -+ if (stat_count <= 0) { -+ return 0; -+ } -+ -+ if (stat_count > node->num_counters) { -+ stat_count = node->num_counters; -+ } -+ -+ hdr.magic = PROF_MAGIC_COUNTERS; -+ hdr.ultra_sample_time = node->clocks; -+ hdr.ultra_count = stat_count; -+ hdr.linux_sample_time = UBICOM32_IO_TIMER->sysval; -+ hdr.linux_count = builtin_count; -+ memcpy(ptr, (void *)&hdr, sizeof(struct profile_header_counters)); -+ ptr += sizeof(struct profile_header_counters); -+ -+ -+ for (i = 0; i < stat_count; ++i) { -+ memcpy(ptr, (void *)(&(node->counters[i])), sizeof(struct profile_counter)); -+ ptr += sizeof(struct profile_counter); -+ } -+ -+ /* -+ * built in statistics -+ */ -+ profile_get_memory_stats(&total_free, &max_free); -+ profile_builtin_stats[0].value = total_free; -+ profile_builtin_stats[1].value = max_free; -+ memcpy(ptr, (void *)profile_builtin_stats, sizeof(profile_builtin_stats)); -+ ptr += sizeof(profile_builtin_stats); -+ -+ if (copy_to_user(buf, prof_pkt, ptr - prof_pkt) != 0) { -+ return -EFAULT; -+ } -+ return ptr - prof_pkt; -+} -+ -+/* -+ * return a udp packet ready to send to the profiler tool -+ * when there are no packets left to make, return 0 -+ */ -+static int profile_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) -+{ -+ int result = 0; -+ if (!node) { -+ return -ENOENT; -+ } -+ node->busy = 1; -+ if (!node->enabled) { -+ node->busy = 0; -+ return -EPERM; -+ } -+ if (!node->samples) { -+ node->busy = 0; -+ return -ENOMEM; -+ } -+ -+ if (profile_first_packet) { -+ result = profile_make_stats_packet(buf, count); -+ profile_first_packet = 0; -+ } -+ if (result == 0) { -+ result = profile_make_data_packet(buf, count); -+ if (result == 0) { -+ profile_first_packet = 1; -+ } -+ } -+ node->busy = 0; -+ return result; -+ -+} -+ -+static int profile_release(struct inode *inode, struct file *filp) -+{ -+ if (!node) { -+ return -ENOENT; -+ } -+ node->busy = 1; -+ if (node->enabled) { -+ node->enabled = 0; -+ node->count = 0; -+ node->tail = node->head; -+ node->busy = 0; -+ return 0; -+ } -+ node->busy = 0; -+ profile_first_packet = 1; -+ return -EBADF; -+} -+ -+static const struct file_operations profile_fops = { -+ .open = profile_open, -+ .read = profile_read, -+ .release = profile_release, -+}; -+ -+static int page_aligned(void *x) -+{ -+ return !((unsigned int)x & ((1 << PAGE_SHIFT) - 1)); -+} -+ -+static int profile_maps_open(struct inode *inode, struct file *filp) -+{ -+ struct rb_node *rb; -+ int num = 0; -+ int slab_start; -+ struct vm_area_struct *vma; -+ int type = PROFILE_MAP_TYPE_UNKNOWN; -+ int flags, i; -+ struct list_head *p; -+ struct zone *zone; -+ -+ /* -+ * get the slab data (first so dups will show up as vmas) -+ */ -+ slab_start = num; -+ num += kmem_cache_block_info("size-512", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ num += kmem_cache_block_info("size-1024", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ num += kmem_cache_block_info("size-2048", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ num += kmem_cache_block_info("size-4096", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ num += kmem_cache_block_info("size-8192", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ -+ for (i = slab_start; i < num; ++i) { -+ profile_pm[i].type_size |= PROFILE_MAP_TYPE_SMALL << PROFILE_MAP_TYPE_SHIFT; -+ } -+ -+ slab_start = num; -+ num += kmem_cache_block_info("dentry", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ num += kmem_cache_block_info("inode_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ num += kmem_cache_block_info("sysfs_dir_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ num += kmem_cache_block_info("proc_inode_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num); -+ -+ for (i = slab_start; i < num; ++i) { -+ profile_pm[i].type_size |= PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT; -+ } -+ -+ /* -+ * get all the vma regions (allocated by mmap, most likely -+ */ -+#if 0 -+ down_read(&nommu_vma_sem); -+ for (rb = rb_first(&nommu_vma_tree); rb && num < PROFILE_NUM_MAPS; rb = rb_next(rb)) { -+ vma = rb_entry(rb, struct vm_area_struct, vm_rb); -+ profile_pm[num].start = (vma->vm_start - SDRAMSTART) >> PAGE_SHIFT; -+ profile_pm[num].type_size = (vma->vm_end - vma->vm_start + (1 << PAGE_SHIFT) - 1) >> PAGE_SHIFT; -+ flags = vma->vm_flags & 0xf; -+ if (flags == (VM_READ | VM_EXEC)) { -+ type = PROFILE_MAP_TYPE_TEXT; -+ } else if (flags == (VM_READ | VM_WRITE | VM_EXEC)) { -+ type = PROFILE_MAP_TYPE_STACK; -+ } else if (flags == (VM_READ | VM_WRITE)) { -+ type = PROFILE_MAP_TYPE_APP_DATA; -+ } -+ profile_pm[num].type_size |= type << PROFILE_MAP_TYPE_SHIFT; -+ num++; -+ } -+ up_read(&nommu_vma_sem); -+ if (rb) { -+ return -ENOMEM; -+ } -+#endif -+ -+ /* -+ * get all the free regions. In each zone, the array of free_area lists contains the first page of each frame of size 1 << order -+ */ -+ for_each_zone(zone) { -+ unsigned long order, flags, i; -+ struct page *page; -+ -+ if (!populated_zone(zone)) -+ continue; -+ -+ if (!is_normal(zone)) -+ continue; -+ -+ spin_lock_irqsave(&zone->lock, flags); -+ for_each_migratetype_order(order, i) { -+ list_for_each(p, &(zone->free_area[order].free_list[i])) { -+ page = list_entry(p, struct page, lru); -+ profile_pm[num].start = ((page_to_phys(page) - SDRAMSTART) >> PAGE_SHIFT) - 0x40; -+ profile_pm[num].type_size = (PROFILE_MAP_TYPE_FREE << PROFILE_MAP_TYPE_SHIFT) | order; -+ num++; -+ if (num >= PROFILE_NUM_MAPS) { -+ spin_unlock_irqrestore(&zone->lock, flags); -+ return -ENOMEM; -+ } -+ } -+ } -+ spin_unlock_irqrestore(&zone->lock, flags); -+ } -+ -+ /* -+ * get the filesystem inodes -+ */ -+ list_for_each(p, &(super_blocks)) { -+ struct super_block *sb; -+ struct list_head *q; -+ if (num >= PROFILE_NUM_MAPS) -+ break; -+ sb = list_entry(p, struct super_block, s_list); -+ if (page_aligned(sb)) { -+ profile_pm[num].start = ((unsigned int)sb - SDRAMSTART) >> PAGE_SHIFT; -+ profile_pm[num].type_size = (PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT); -+ num++; -+ } -+ list_for_each(q, &(sb->s_inodes)) { -+ struct inode *in; -+ if (num >= PROFILE_NUM_MAPS) -+ break; -+ in = list_entry(q, struct inode, i_sb_list); -+ if (page_aligned(in)) { -+ profile_pm[num].start = ((unsigned int)in - SDRAMSTART) >> PAGE_SHIFT; -+ profile_pm[num].type_size = (PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT); -+ num++; -+ } -+ } -+ } -+ -+ /* -+ * get the buffer cache pages -+ */ -+ for (i = 0; i < num_physpages && num < PROFILE_NUM_MAPS; ++i) { -+ if ((mem_map + i)->flags & (1 << PG_lru)) { -+ int start = i; -+ while ((mem_map + i)->flags & (1 << PG_lru) && i < num_physpages) -+ i++; -+ profile_pm[num].start = start; -+ profile_pm[num].type_size = (i - start) | (PROFILE_MAP_TYPE_CACHE << PROFILE_MAP_TYPE_SHIFT); -+ num++; -+ } -+ } -+ -+ filp->private_data = (void *)num; -+ return 0; -+} -+ -+/* -+ * return one packet of map data, or 0 if all maps have been returned already -+ */ -+static int profile_maps_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) -+{ -+ struct profile_header_maps header; -+ char *p = buf + sizeof(header); -+ int total = (int)filp->private_data; -+ -+ header.count = (count - sizeof(header)) / sizeof(struct profile_map); -+ if (header.count > PROFILE_MAX_MAPS) { -+ header.count = PROFILE_MAX_MAPS;; -+ } -+ if (header.count > total - *f_pos) { -+ header.count = total - *f_pos; -+ } -+ -+ if (header.count == 0) { -+ return 0; -+ } -+ -+ header.magic = PROF_MAGIC_MAPS; -+ header.page_shift = PAGE_SHIFT; -+ -+ if (copy_to_user(buf, &header, sizeof(header)) != 0) { -+ return -EFAULT; -+ } -+ if (copy_to_user(p, (void *)&profile_pm[*f_pos], sizeof(struct profile_map) * header.count) != 0) { -+ return -EFAULT; -+ } -+ *f_pos += header.count; -+ -+ return sizeof(header) + sizeof(struct profile_map) * header.count; -+} -+ -+static int profile_maps_release(struct inode *inode, struct file *filp) -+{ -+ return 0; -+} -+ -+static const struct file_operations profile_maps_fops = { -+ .open = profile_maps_open, -+ .read = profile_maps_read, -+ .release = profile_maps_release, -+}; -+ -+static int profile_rate_show(struct seq_file *m, void *v) -+{ -+ if (node) { -+ seq_printf(m, "%d samples per second. %d virtual counters.\n", node->rate, node->num_counters); -+ } else { -+ seq_printf(m, "Profiler is not initialized.\n"); -+ } -+ return 0; -+} -+ -+static int profile_rate_open(struct inode *inode, struct file *filp) -+{ -+ return single_open(filp, profile_rate_show, NULL); -+} -+ -+static int profile_rate_write(struct file *filp, const char *buf, size_t len, loff_t *off) -+{ -+ *off = 0; -+ return 0; -+} -+ -+static const struct file_operations profile_rate_fops = { -+ .open = profile_rate_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+ .write = profile_rate_write, -+}; -+ -+int ubi32_profile_init_module(void) -+{ -+ struct proc_dir_entry *pdir; -+ -+ /* -+ * find the device -+ */ -+ node = (struct profilenode *)devtree_find_node("profiler"); -+ if (!node) { -+ printk(KERN_INFO "Profiler does not exist.\n"); -+ return -ENODEV; -+ } -+ -+ /* -+ * allocate the sample buffer -+ */ -+ node->max_samples = PROFILE_MAX_SAMPLES; -+ node->samples = kmalloc(node->max_samples * sizeof(struct profile_sample), GFP_KERNEL); -+ if (!node->samples) { -+ printk(KERN_INFO "Profiler sample buffer kmalloc failed.\n"); -+ return -ENOMEM; -+ } -+ -+ /* -+ * connect to the file system -+ */ -+ pdir = proc_mkdir("profile", NULL); -+ if (!pdir) { -+ return -ENOMEM; -+ } -+ if (!proc_create("data", 0, pdir, &profile_fops)) { -+ return -ENOMEM; -+ } -+ if (!proc_create("rate", 0, pdir, &profile_rate_fops)) { -+ return -ENOMEM; -+ } -+ if (!proc_create("maps", 0, pdir, &profile_maps_fops)) { -+ return -ENOMEM; -+ } -+ return 0; -+} -+ -+ -+module_init(ubi32_profile_init_module); -+ -+MODULE_AUTHOR("David Fotland"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/arch/ubicom32/mach-common/profile.h -@@ -0,0 +1,82 @@ -+/* -+ * arch/ubicom32/mach-common/profile.h -+ * Private data for the profile module -+ * -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include "profpkt.h" -+ -+#ifndef _PROFILE_H_ -+#define _PROFILE_H_ -+ -+#define PROFILE_MAX_THREADS 16 -+#define PROFILE_MAX_SAMPLES 1024 -+ -+struct profile_sample; -+struct oprofile_sample; -+ -+/* -+ * values chosen so all counter values fit in a single UDP packet -+ */ -+#define PROFILE_NODE_MAX_COUNTERS 32 -+ -+struct profile_counter { -+ char name[PROFILE_COUNTER_NAME_LENGTH]; -+ unsigned int value; -+}; -+ -+struct profilenode { -+ struct devtree_node dn; -+ volatile u32_t enabled; /* Is the profiler enabled to take samples? */ -+ volatile u32_t busy; /* set when the samples are being read by the driver */ -+ volatile u32_t rate; /* What is the sampling rate? */ -+ volatile u32_t enabled_threads; /* which threads were enabled at the last sample time */ -+ volatile u32_t hrt; /* HRT threads */ -+ volatile u32_t profiler_thread; /* thread running the profile sampler */ -+ volatile u32_t clocks; /* system clock timer at last sample */ -+ volatile u32_t clock_freq; /* clock frequency in Hz */ -+ volatile u32_t ddr_freq; /* memory frequency */ -+ volatile u32_t cpu_id; /* chip_id register */ -+ volatile u32_t inst_count[PROFILE_MAX_THREADS]; /* sampled instruction counts at most recent sample */ -+ volatile u32_t stats[4]; /* contents of the cache statistics counters */ -+ volatile u16_t head; /* sample taker puts samples here */ -+ volatile u16_t tail; /* packet filler takes samples here */ -+ volatile u16_t count; /* number of valid samples */ -+ volatile u16_t max_samples; /* how many samples can be in the samples array */ -+ struct profile_sample *samples; /* samples array allocated by the linux driver */ -+ volatile u32_t num_counters; /* how many registered performance counters */ -+ volatile struct profile_counter counters[PROFILE_NODE_MAX_COUNTERS]; -+ -+ /* unimplemented interface for future oprofile work */ -+ volatile u16_t oprofile_head; /* sample taker puts samples here */ -+ volatile u16_t oprofile_tail; /* packet filler takes samples here */ -+ volatile u16_t oprofile_count; /* how many oprofile sampels are are in use */ -+ volatile u16_t oprofile_max_samples; /* samples array size for oprofile samples */ -+ struct oprofile_sample *oprofile_samples; /* oprofile sample buffer */ -+}; -+ -+#endif ---- /dev/null -+++ b/arch/ubicom32/mach-common/profpkt.h -@@ -0,0 +1,158 @@ -+ -+/* -+ * arch/ubicom32/mach-common/profpkt.c -+ * Ubicom32 Profiler packet formats for communication between the linux proc driver and the profiler display tool -+ * -+ * (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 . -+ */ -+ -+#define PROFILE_PORT 51080 -+#define PROFILE_POSIX_NAME_LENGTH 32 -+ -+/* -+ * profile UDP packet format for communicating between ip3k and host -+ * -+ * every packet starts with a header, followed by samples. -+ * samples are only taken for non-hrt threads that are -+ * active -+ */ -+#define PROF_MAGIC 0x3ea0 -+#define PROF_MAGIC_COUNTERS 0x9ea0 -+#define PROF_MAGIC_MAPS 0xaea0 -+ -+/* -+ * Versions (31 max): -+ * 1 to 4 were before 6.0 release, development versions -+ * 5 was forward compatible version, shipped with 6.0 and 6.1 -+ * 6 adds heap packets, and clock_freq to header, shipped with 6.2 -+ * 7 adds a sequence numbers to check for dropped packets, shipped with 6.3.5 -+ * 8 adds mqueue timing information, shipped with 6.3.5 -+ * 9 adds sdram heap size information, shipped with 6.4 -+ * 10 adds heapmem heap callers and long latency stack traces. shipped with 6.4 -+ * 11 adds support for Mars (IP5K). shipped with 6.10 -+ * 12 adds more support for Mars. Shipped with 7.0 -+ * 13 adds per sample latency measurement. Shipped with 7.2 -+ * 14 changes the heap format and adds a string packet. Shipped with 7.4 -+ * 15 adds dsr stats and posix. shipped with 7.6 -+ * 16 corrects maximum packet count for Ares. ships with 7.9 -+ * 17 adds a5 register value to sample -+ */ -+ -+#define PROFILE_VERSION 17 -+#define PROFILE_MAX_PACKET_SIZE 1440 -+ -+#define PROFILE_MAX_THREADS 16 -+ -+/* -+ * each packet starts with a profile_header, then sample_count samples -+ * samples are gprof samples of pc, the return address, condition codes, and -+ * active threads -+ */ -+struct profile_header { -+ u16_t magic; /* magic number and version */ -+ u8_t header_size; /* number of bytes in profile header */ -+ u8_t sample_count; /* number of samples in the packet */ -+ u32_t clocks; /* clock counter value */ -+ u32_t instruction_count[PROFILE_MAX_THREADS]; -+ /* instructions executed per thread */ -+ u32_t profile_instructions; /* instructions executed by profiler mainline */ -+ u16_t enabled; /* which threads are enabled */ -+ u16_t hrt; /* which threads are hrt */ -+ u16_t high; /* which threads are high priority */ -+ u16_t profiler_thread; /* which thread runs the profiler */ -+ u32_t heap_free; /* current free on-cihp heap space in bytes */ -+ u32_t heap_low_water; /* on-chip heap low water mark */ -+ u32_t netpage_free; /* number of free on-chip net pages */ -+ u32_t netpage_low_water; /* low water mark on free on-chip netpages */ -+ u32_t min_sp[PROFILE_MAX_THREADS]; -+ /* stack pointer values per thread */ -+ u32_t clock_freq; /* clock frequency (Hz) of system being analyzed */ -+ u32_t seq_num; /* to detect dropped profiler packets */ -+ u32_t timing_sequence; /* sample number since boot */ -+ u32_t timing_interval; /* second per sample timing interval */ -+ u32_t timing_worst_time; /* duration of longest finction called, in core clocks */ -+ u32_t timing_function; /* address of longest function */ -+ u32_t timing_average; /* average time of all functions in last interval */ -+ u32_t timing_count; /* number of functions called in last interval */ -+ u32_t extheap_free; /* current free extmem heap space in bytes */ -+ u32_t extheap_low_water; /* extmem heap low water mark */ -+ u32_t cpu_id; /* CHIP_ID register contents */ -+ u32_t perf_counters[4]; /* contents of the CPU performance counters */ -+ u8_t perf_config[4]; /* what is being counted */ -+ u32_t ddr_freq; /* DDR clock frequency */ -+ u32_t extnetpage_free; /* number of free off chip net pages */ -+ u32_t extnetpage_low_water; /* low water mark on off-chip free netpages */ -+ u32_t dsr_max_latency; /* max time to process a dsr interrupt, in clocks, since last packet */ -+ u32_t dsr_ave_latency; /* average dsr latency over last DSR_STATS_RECENT_COUNT interrupts */ -+ u32_t dsr_count; /* number of dsr interrupts since last packet */ -+}; -+ -+struct profile_header_counters { -+ u16_t magic; -+ u16_t ultra_count; /* how many ultra counters follow this */ -+ u32_t ultra_sample_time; /* in chip clocks */ -+ u32_t linux_count; /* how many linux counters follow this */ -+ u32_t linux_sample_time; -+}; -+ -+/* -+ * values chosen so all counter values fit in a single 1400 byte UDP packet -+ */ -+#define PROFILE_COUNTER_NAME_LENGTH 20 -+#define PROFILE_MAX_COUNTERS ((PROFILE_MAX_PACKET_SIZE - sizeof(struct profile_header_counters)) / (PROFILE_COUNTER_NAME_LENGTH + 4)) -+ -+struct profile_counter_pkt { -+ char name[PROFILE_COUNTER_NAME_LENGTH]; -+ unsigned int value; -+}; -+ -+/* -+ * send memory maps from linux to profiler tool -+ */ -+ -+struct profile_header_maps { -+ u16_t magic; -+ u16_t count; -+ u32_t page_shift; -+}; -+ -+#define PROFILE_MAP_NUM_TYPES 32 -+ -+/* types 0-15: size field is order. True size is 2^order */ -+#define PROFILE_MAP_TYPE_UNKNOWN 0 -+#define PROFILE_MAP_TYPE_FREE 1 -+#define PROFILE_MAP_TYPE_SMALL 2 -+#define PROFILE_MAP_TYPE_FS 3 -+/* types 16-31: size field is pages. True size is (1 << PAGE_SHIFT) * size */ -+#define PROFILE_MAP_SIZE_TYPE 16 -+#define PROFILE_MAP_TYPE_TEXT 16 -+#define PROFILE_MAP_TYPE_STACK 17 -+#define PROFILE_MAP_TYPE_APP_DATA 18 -+#define PROFILE_MAP_TYPE_CACHE 19 -+#define PROFILE_MAP_RESERVED 24 -+ -+#define PROFILE_MAP_TYPE_SHIFT 11 -+#define PROFILE_MAP_SIZE_MASK 0x7ff -+ -+struct profile_map { -+ u16_t start; /* start page number of segment, relative to start of DRAM */ -+ u16_t type_size; /* type (4 bits) of the segment and size in pages (12 bits) */ -+}; -+ -+#define PROFILE_MAX_MAPS (PROFILE_MAX_PACKET_SIZE - sizeof(struct profile_header_maps)) / sizeof(struct profile_map) ---- /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 . -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+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/switch-bcm539x.c -@@ -0,0 +1,1195 @@ -+/* -+ * arch/ubicom32/mach-common/switch-bcm539x.c -+ * BCM539X switch driver, SPI mode -+ * -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include "switch-core.h" -+#include "switch-bcm539x-reg.h" -+ -+#define DRIVER_NAME "bcm539x-spi" -+#define DRIVER_VERSION "1.0" -+ -+#undef BCM539X_DEBUG -+#define BCM539X_SPI_RETRIES 100 -+ -+struct bcm539x_data { -+ struct switch_device *switch_dev; -+ -+ /* -+ * Our private data -+ */ -+ struct spi_device *spi; -+ struct switch_core_platform_data *pdata; -+ -+ /* -+ * Last page we accessed -+ */ -+ u8_t last_page; -+ -+ /* -+ * 539x Device ID -+ */ -+ u8_t device_id; -+}; -+ -+/* -+ * bcm539x_wait_status -+ * Waits for the specified bit in the status register to be set/cleared. -+ */ -+static int bcm539x_wait_status(struct bcm539x_data *bd, u8_t mask, int set) -+{ -+ u8_t txbuf[2]; -+ u8_t rxbuf; -+ int i; -+ int ret; -+ -+ txbuf[0] = BCM539X_CMD_READ; -+ txbuf[1] = BCM539X_GLOBAL_SPI_STATUS; -+ for (i = 0; i < BCM539X_SPI_RETRIES; i++) { -+ ret = spi_write_then_read(bd->spi, txbuf, 2, &rxbuf, 1); -+ rxbuf &= mask; -+ if ((set && rxbuf) || (!set && !rxbuf)) { -+ return 0; -+ } -+ udelay(1); -+ } -+ -+ return -EIO; -+} -+ -+/* -+ * bcm539x_set_page -+ * Sets the register page for access (only if necessary) -+ */ -+static int bcm539x_set_page(struct bcm539x_data *bd, u8_t page) -+{ -+ u8_t txbuf[3]; -+ -+ if (page == bd->last_page) { -+ return 0; -+ } -+ -+ bd->last_page = page; -+ -+ txbuf[0] = BCM539X_CMD_WRITE; -+ txbuf[1] = BCM539X_GLOBAL_PAGE; -+ txbuf[2] = page; -+ -+ return spi_write(bd->spi, txbuf, 3); -+} -+ -+/* -+ * bcm539x_write_bytes -+ * Writes a number of bytes to a given page and register -+ */ -+static int bcm539x_write_bytes(struct bcm539x_data *bd, u8_t page, -+ u8_t reg, void *buf, u8_t len) -+{ -+ int ret; -+ u8_t *txbuf; -+ -+ txbuf = kmalloc(2 + len, GFP_KERNEL); -+ if (!txbuf) { -+ return -ENOMEM; -+ } -+ -+ /* -+ * Make sure the chip has finished processing our previous request -+ */ -+ ret = bcm539x_wait_status(bd, BCM539X_GLOBAL_SPI_ST_SPIF, 0); -+ if (ret) { -+ goto done; -+ } -+ -+ /* -+ * Set the page -+ */ -+ ret = bcm539x_set_page(bd, page); -+ if (ret) { -+ goto done; -+ } -+ -+ /* -+ * Read the data -+ */ -+ txbuf[0] = BCM539X_CMD_WRITE; -+ txbuf[1] = reg; -+ memcpy(&txbuf[2], buf, len); -+ -+#ifdef BCM539X_DEBUG -+ { -+ int i; -+ printk("write page %02x reg %02x len=%d buf=", page, reg, len); -+ for (i = 0; i < len + 2; i++) { -+ printk("%02x ", txbuf[i]); -+ } -+ printk("\n"); -+ } -+#endif -+ -+ ret = spi_write(bd->spi, txbuf, 2 + len); -+ -+done: -+ kfree(txbuf); -+ return ret; -+} -+ -+/* -+ * bcm539x_write_32 -+ * Writes 32 bits of data to the given page and register -+ */ -+static inline int bcm539x_write_32(struct bcm539x_data *bd, u8_t page, -+ u8_t reg, u32_t data) -+{ -+ data = cpu_to_le32(data); -+ return bcm539x_write_bytes(bd, page, reg, &data, 4); -+} -+ -+/* -+ * bcm539x_write_16 -+ * Writes 16 bits of data to the given page and register -+ */ -+static inline int bcm539x_write_16(struct bcm539x_data *bd, u8_t page, -+ u8_t reg, u16_t data) -+{ -+ data = cpu_to_le16(data); -+ return bcm539x_write_bytes(bd, page, reg, &data, 2); -+} -+ -+/* -+ * bcm539x_write_8 -+ * Writes 8 bits of data to the given page and register -+ */ -+static inline int bcm539x_write_8(struct bcm539x_data *bd, u8_t page, -+ u8_t reg, u8_t data) -+{ -+ return bcm539x_write_bytes(bd, page, reg, &data, 1); -+} -+ -+/* -+ * bcm539x_read_bytes -+ * Reads a number of bytes from a given page and register -+ */ -+static int bcm539x_read_bytes(struct bcm539x_data *bd, u8_t page, -+ u8_t reg, void *buf, u8_t len) -+{ -+ u8_t txbuf[2]; -+ int ret; -+ -+ /* -+ * (1) Make sure the chip has finished processing our previous request -+ */ -+ ret = bcm539x_wait_status(bd, BCM539X_GLOBAL_SPI_ST_SPIF, 0); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * (2) Set the page -+ */ -+ ret = bcm539x_set_page(bd, page); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * (3) Kick off the register read -+ */ -+ txbuf[0] = BCM539X_CMD_READ; -+ txbuf[1] = reg; -+ ret = spi_write_then_read(bd->spi, txbuf, 2, txbuf, 1); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * (4) Wait for RACK -+ */ -+ ret = bcm539x_wait_status(bd, BCM539X_GLOBAL_SPI_ST_RACK, 1); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * (5) Read the data -+ */ -+ txbuf[0] = BCM539X_CMD_READ; -+ txbuf[1] = BCM539X_GLOBAL_SPI_DATA0; -+ -+ ret = spi_write_then_read(bd->spi, txbuf, 2, buf, len); -+ -+#ifdef BCM539X_DEBUG -+ { -+ int i; -+ printk("read page %02x reg %02x len=%d rxbuf=", -+ page, reg, len); -+ for (i = 0; i < len; i++) { -+ printk("%02x ", ((u8_t *)buf)[i]); -+ } -+ printk("\n"); -+ } -+#endif -+ -+ return ret; -+} -+ -+/* -+ * bcm539x_read_32 -+ * Reads an 32 bit number from a given page and register -+ */ -+static int bcm539x_read_32(struct bcm539x_data *bd, u8_t page, -+ u8_t reg, u32_t *buf) -+{ -+ int ret = bcm539x_read_bytes(bd, page, reg, buf, 4); -+ *buf = le32_to_cpu(*buf); -+ return ret; -+} -+ -+/* -+ * bcm539x_read_16 -+ * Reads an 16 bit number from a given page and register -+ */ -+static int bcm539x_read_16(struct bcm539x_data *bd, u8_t page, -+ u8_t reg, u16_t *buf) -+{ -+ int ret = bcm539x_read_bytes(bd, page, reg, buf, 2); -+ *buf = le16_to_cpu(*buf); -+ return ret; -+} -+ -+/* -+ * bcm539x_read_8 -+ * Reads an 8 bit number from a given page and register -+ */ -+static int bcm539x_read_8(struct bcm539x_data *bd, u8_t page, -+ u8_t reg, u8_t *buf) -+{ -+ return bcm539x_read_bytes(bd, page, reg, buf, 1); -+} -+ -+/* -+ * bcm539x_set_mode -+ */ -+static int bcm539x_set_mode(struct bcm539x_data *bd, int state) -+{ -+ u8_t buf; -+ int ret; -+ -+ ret = bcm539x_read_8(bd, PAGE_PORT_TC, REG_CTRL_MODE, &buf); -+ if (ret) { -+ return ret; -+ } -+ -+ buf &= ~(1 << 1); -+ buf |= state ? (1 << 1) : 0; -+ -+ ret = bcm539x_write_8(bd, PAGE_PORT_TC, REG_CTRL_MODE, buf); -+ return ret; -+} -+ -+/* -+ * bcm539x_handle_reset -+ */ -+static int bcm539x_handle_reset(struct switch_device *dev, char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ int ret; -+ -+ ret = bcm539x_write_8(bd, PAGE_PORT_TC, REG_CTRL_SRST, -+ (1 << 7) | (1 << 4)); -+ if (ret) { -+ return ret; -+ } -+ -+ udelay(20); -+ -+ ret = bcm539x_write_8(bd, PAGE_PORT_TC, REG_CTRL_SRST, 0); -+ return ret; -+} -+ -+/* -+ * bcm539x_handle_vlan_ports_read -+ */ -+static int bcm539x_handle_vlan_ports_read(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ int j; -+ int len = 0; -+ u8_t rxbuf8; -+ u32_t rxbuf32; -+ int ret; -+ -+ ret = bcm539x_write_16(bd, PAGE_VTBL, REG_VTBL_INDX_5395, inst); -+ if (ret) { -+ return ret; -+ } -+ -+ ret = bcm539x_write_8(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, -+ (1 << 7) | 1); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * Wait for completion -+ */ -+ for (j = 0; j < BCM539X_SPI_RETRIES; j++) { -+ ret = bcm539x_read_8(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, -+ &rxbuf8); -+ if (ret) { -+ return ret; -+ } -+ if (!(rxbuf8 & (1 << 7))) { -+ break; -+ } -+ } -+ -+ if (j == BCM539X_SPI_RETRIES) { -+ return -EIO; -+ } -+ -+ /* -+ * Read the table entry -+ */ -+ ret = bcm539x_read_32(bd, PAGE_VTBL, REG_VTBL_ENTRY_5395, &rxbuf32); -+ if (ret) { -+ return ret; -+ } -+ -+ for (j = 0; j < 9; j++) { -+ if (rxbuf32 & (1 << j)) { -+ u16_t rxbuf16; -+ len += sprintf(buf + len, "%d", j); -+ if (rxbuf32 & (1 << (j + 9))) { -+ buf[len++] = 'u'; -+ } else { -+ buf[len++] = 't'; -+ } -+ ret = bcm539x_read_16(bd, PAGE_VLAN, -+ REG_VLAN_PTAG0 + (j << 1), -+ &rxbuf16); -+ if (ret) { -+ return ret; -+ } -+ if (rxbuf16 == inst) { -+ buf[len++] = '*'; -+ } -+ buf[len++] = '\t'; -+ } -+ } -+ -+ len += sprintf(buf + len, "\n"); -+ buf[len] = '\0'; -+ -+ return len; -+} -+ -+/* -+ * bcm539x_handle_vlan_ports_write -+ */ -+static int bcm539x_handle_vlan_ports_write(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ int j; -+ u32_t untag; -+ u32_t ports; -+ u32_t def; -+ -+ u8_t rxbuf8; -+ u16_t rxbuf16; -+ int ret; -+ -+ switch_parse_vlan_ports(dev, buf, &untag, &ports, &def); -+ -+#ifdef BCM539X_DEBUG -+ printk(KERN_DEBUG "'%s' inst=%d untag=%08x ports=%08x def=%08x\n", -+ buf, inst, untag, ports, def); -+#endif -+ -+ if (!ports) { -+ return 0; -+ } -+ -+ /* -+ * Change default vlan tag -+ */ -+ for (j = 0; j < 9; j++) { -+ if ((untag | def) & (1 << j)) { -+ ret = bcm539x_write_16(bd, PAGE_VLAN, -+ REG_VLAN_PTAG0 + (j << 1), -+ inst); -+ if (ret) { -+ return ret; -+ } -+ continue; -+ } -+ -+ if (!(dev->port_mask[0] & (1 << j))) { -+ continue; -+ } -+ -+ /* -+ * Remove any ports which are not listed anymore as members of -+ * this vlan -+ */ -+ ret = bcm539x_read_16(bd, PAGE_VLAN, -+ REG_VLAN_PTAG0 + (j << 1), &rxbuf16); -+ if (ret) { -+ return ret; -+ } -+ if (rxbuf16 == inst) { -+ ret = bcm539x_write_16(bd, PAGE_VLAN, -+ REG_VLAN_PTAG0 + (j << 1), 0); -+ if (ret) { -+ return ret; -+ } -+ } -+ } -+ -+ /* -+ * Write the VLAN table -+ */ -+ ret = bcm539x_write_16(bd, PAGE_VTBL, REG_VTBL_INDX_5395, inst); -+ if (ret) { -+ return ret; -+ } -+ -+ ret = bcm539x_write_32(bd, PAGE_VTBL, REG_VTBL_ENTRY_5395, -+ (untag << 9) | ports); -+ if (ret) { -+ return ret; -+ } -+ -+ ret = bcm539x_write_8(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, -+ (1 << 7) | 0); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * Wait for completion -+ */ -+ for (j = 0; j < BCM539X_SPI_RETRIES; j++) { -+ ret = bcm539x_read_bytes(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, -+ &rxbuf8, 1); -+ if (ret) { -+ return ret; -+ } -+ if (!(rxbuf8 & (1 << 7))) { -+ break; -+ } -+ } -+ -+ return (j < BCM539X_SPI_RETRIES) ? 0 : -EIO; -+} -+ -+/* -+ * Handlers for /vlan/ -+ */ -+static const struct switch_handler bcm539x_switch_handlers_vlan_dir[] = { -+ { -+ .name = "ports", -+ .read = bcm539x_handle_vlan_ports_read, -+ .write = bcm539x_handle_vlan_ports_write, -+ }, -+ { -+ }, -+}; -+ -+/* -+ * bcm539x_handle_vlan_delete_write -+ */ -+static int bcm539x_handle_vlan_delete_write(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ int vid; -+ u8_t rxbuf8; -+ u32_t txbuf; -+ int j; -+ int ret; -+ -+ vid = simple_strtoul(buf, NULL, 0); -+ if (!vid) { -+ return -EINVAL; -+ } -+ -+ /* -+ * Disable this VLAN -+ * -+ * Go through the port-based vlan registers and clear the appropriate -+ * ones out -+ */ -+ for (j = 0; j < 9; j++) { -+ u16_t rxbuf16; -+ ret = bcm539x_read_16(bd, PAGE_VLAN, REG_VLAN_PTAG0 + (j << 1), -+ &rxbuf16); -+ if (ret) { -+ return ret; -+ } -+ if (rxbuf16 == vid) { -+ txbuf = 0; -+ ret = bcm539x_write_16(bd, PAGE_VLAN, -+ REG_VLAN_PTAG0 + (j << 1), -+ txbuf); -+ if (ret) { -+ return ret; -+ } -+ } -+ } -+ -+ /* -+ * Write the VLAN table -+ */ -+ txbuf = vid; -+ ret = bcm539x_write_16(bd, PAGE_VTBL, REG_VTBL_INDX_5395, txbuf); -+ if (ret) { -+ return ret; -+ } -+ -+ txbuf = 0; -+ ret = bcm539x_write_32(bd, PAGE_VTBL, REG_VTBL_ENTRY_5395, txbuf); -+ if (ret) { -+ return ret; -+ } -+ -+ txbuf = (1 << 7) | (0); -+ ret = bcm539x_write_8(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, txbuf); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * Wait for completion -+ */ -+ for (j = 0; j < BCM539X_SPI_RETRIES; j++) { -+ ret = bcm539x_read_bytes(bd, PAGE_VTBL, REG_VTBL_ACCESS_5395, -+ &rxbuf8, 1); -+ if (ret) { -+ return ret; -+ } -+ if (!(rxbuf8 & (1 << 7))) { -+ break; -+ } -+ } -+ -+ if (j == BCM539X_SPI_RETRIES) { -+ return -EIO; -+ } -+ -+ return switch_remove_vlan_dir(dev, vid); -+} -+ -+/* -+ * bcm539x_handle_vlan_create_write -+ */ -+static int bcm539x_handle_vlan_create_write(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ int vid; -+ -+ vid = simple_strtoul(buf, NULL, 0); -+ if (!vid) { -+ return -EINVAL; -+ } -+ -+ return switch_create_vlan_dir(dev, vid, -+ bcm539x_switch_handlers_vlan_dir); -+} -+ -+/* -+ * bcm539x_handle_enable_read -+ */ -+static int bcm539x_handle_enable_read(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ u8_t rxbuf; -+ int ret; -+ -+ ret = bcm539x_read_8(bd, PAGE_PORT_TC, REG_CTRL_MODE, &rxbuf); -+ if (ret) { -+ return ret; -+ } -+ rxbuf = (rxbuf & (1 << 1)) ? 1 : 0; -+ -+ return sprintf(buf, "%d\n", rxbuf); -+} -+ -+/* -+ * bcm539x_handle_enable_write -+ */ -+static int bcm539x_handle_enable_write(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ -+ return bcm539x_set_mode(bd, buf[0] == '1'); -+} -+ -+/* -+ * bcm539x_handle_enable_vlan_read -+ */ -+static int bcm539x_handle_enable_vlan_read(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ u8_t rxbuf; -+ int ret; -+ -+ ret = bcm539x_read_8(bd, PAGE_VLAN, REG_VLAN_CTRL0, &rxbuf); -+ if (ret) { -+ return ret; -+ } -+ rxbuf = (rxbuf & (1 << 7)) ? 1 : 0; -+ -+ return sprintf(buf, "%d\n", rxbuf); -+} -+ -+/* -+ * bcm539x_handle_enable_vlan_write -+ */ -+static int bcm539x_handle_enable_vlan_write(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ int ret; -+ -+ /* -+ * disable 802.1Q VLANs -+ */ -+ if (buf[0] != '1') { -+ ret = bcm539x_write_8(bd, PAGE_VLAN, REG_VLAN_CTRL0, 0); -+ return ret; -+ } -+ -+ /* -+ * enable 802.1Q VLANs -+ * -+ * Enable 802.1Q | IVL learning -+ */ -+ ret = bcm539x_write_8(bd, PAGE_VLAN, REG_VLAN_CTRL0, -+ (1 << 7) | (3 << 5)); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * RSV multicast fwd | RSV multicast chk -+ */ -+ ret = bcm539x_write_8(bd, PAGE_VLAN, REG_VLAN_CTRL1, -+ (1 << 2) | (1 << 3)); -+ if (ret) { -+ return ret; -+ } -+#if 0 -+ /* -+ * Drop invalid VID -+ */ -+ ret = bcm539x_write_16(bd, PAGE_VLAN, REG_VLAN_CTRL3, 0x00FF); -+ if (ret) { -+ return ret; -+ } -+#endif -+ return 0; -+} -+ -+/* -+ * bcm539x_handle_port_enable_read -+ */ -+static int bcm539x_handle_port_enable_read(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ return sprintf(buf, "%d\n", 1); -+} -+ -+/* -+ * bcm539x_handle_port_enable_write -+ */ -+static int bcm539x_handle_port_enable_write(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ /* -+ * validate port -+ */ -+ if (!(dev->port_mask[0] & (1 << inst))) { -+ return -EIO; -+ } -+ -+ if (buf[0] != '1') { -+ printk(KERN_WARNING "switch port[%d] disabling is not supported\n", inst); -+ } -+ return 0; -+} -+ -+/* -+ * bcm539x_handle_port_state_read -+ */ -+static int bcm539x_handle_port_state_read(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ int ret; -+ u16_t link; -+ -+ /* -+ * validate port -+ */ -+ if (!(dev->port_mask[0] & (1 << inst))) { -+ return -EIO; -+ } -+ -+ /* -+ * check PHY link state - CPU port (port 8) is always up -+ */ -+ ret = bcm539x_read_16(bd, PAGE_STATUS, REG_LINK_STATUS, &link); -+ if (ret) { -+ return ret; -+ } -+ link |= (1 << 8); -+ -+ return sprintf(buf, "%d\n", (link & (1 << inst)) ? 1 : 0); -+} -+ -+/* -+ * bcm539x_handle_port_media_read -+ */ -+static int bcm539x_handle_port_media_read(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ int ret; -+ u16_t link, duplex; -+ u32_t speed; -+ -+ /* -+ * validate port -+ */ -+ if (!(dev->port_mask[0] & (1 << inst))) { -+ return -EIO; -+ } -+ -+ /* -+ * check PHY link state first - CPU port (port 8) is always up -+ */ -+ ret = bcm539x_read_16(bd, PAGE_STATUS, REG_LINK_STATUS, &link); -+ if (ret) { -+ return ret; -+ } -+ link |= (1 << 8); -+ -+ if (!(link & (1 << inst))) { -+ return sprintf(buf, "UNKNOWN\n"); -+ } -+ -+ /* -+ * get link speeda dn duplex - CPU port (port 8) is 1000/full -+ */ -+ ret = bcm539x_read_32(bd, PAGE_STATUS, 4, &speed); -+ if (ret) { -+ return ret; -+ } -+ speed |= (2 << 16); -+ speed = (speed >> (2 * inst)) & 3; -+ -+ ret = bcm539x_read_16(bd, PAGE_STATUS, 8, &duplex); -+ if (ret) { -+ return ret; -+ } -+ duplex |= (1 << 8); -+ duplex = (duplex >> inst) & 1; -+ -+ return sprintf(buf, "%d%cD\n", -+ (speed == 0) ? 10 : ((speed == 1) ? 100 : 1000), -+ duplex ? 'F' : 'H'); -+} -+ -+/* -+ * bcm539x_handle_port_meida_write -+ */ -+static int bcm539x_handle_port_meida_write(struct switch_device *dev, -+ char *buf, int inst) -+{ -+ struct bcm539x_data *bd = -+ (struct bcm539x_data *)switch_get_drvdata(dev); -+ int ret; -+ u16_t ctrl_word, local_cap, local_giga_cap; -+ -+ /* -+ * validate port (not for CPU port) -+ */ -+ if (!(dev->port_mask[0] & (1 << inst) & ~(1 << 8))) { -+ return -EIO; -+ } -+ -+ /* -+ * Get the maximum capability from status -+ * SPI reg[0x00] = PHY[0x0] --- MII control -+ * SPI reg[0x08] = PHY[0x4] --- MII local capability -+ * SPI reg[0x12] = PHY[0x9] --- GMII control -+ */ -+ ret = bcm539x_read_16(bd, REG_MII_PAGE + inst, (MII_ADVERTISE << 1), &local_cap); -+ if (ret) { -+ return ret; -+ } -+ ret = bcm539x_read_16(bd, REG_MII_PAGE + inst, (MII_CTRL1000 << 1), &local_giga_cap); -+ if (ret) { -+ return ret; -+ } -+ -+ /* Configure to the requested speed */ -+ if (strncmp(buf, "1000FD", 6) == 0) { -+ /* speed */ -+ local_cap &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL); -+ local_cap &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL); -+ local_giga_cap |= (ADVERTISE_1000HALF | ADVERTISE_1000FULL); -+ /* duplex */ -+ } else if (strncmp(buf, "100FD", 5) == 0) { -+ /* speed */ -+ local_cap &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL); -+ local_cap |= (ADVERTISE_100HALF | ADVERTISE_100FULL); -+ local_giga_cap &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); -+ /* duplex */ -+ local_cap &= ~(ADVERTISE_100HALF); -+ } else if (strncmp(buf, "100HD", 5) == 0) { -+ /* speed */ -+ local_cap &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL); -+ local_cap |= (ADVERTISE_100HALF | ADVERTISE_100FULL); -+ local_giga_cap &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); -+ /* duplex */ -+ local_cap &= ~(ADVERTISE_100FULL); -+ } else if (strncmp(buf, "10FD", 4) == 0) { -+ /* speed */ -+ local_cap |= (ADVERTISE_10HALF | ADVERTISE_10FULL); -+ local_cap &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL); -+ local_giga_cap &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); -+ /* duplex */ -+ local_cap &= ~(ADVERTISE_10HALF); -+ } else if (strncmp(buf, "10HD", 4) == 0) { -+ /* speed */ -+ local_cap |= (ADVERTISE_10HALF | ADVERTISE_10FULL); -+ local_cap &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL); -+ local_giga_cap &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); -+ /* duplex */ -+ local_cap &= ~(ADVERTISE_10FULL); -+ } else if (strncmp(buf, "AUTO", 4) == 0) { -+ /* speed */ -+ local_cap |= (ADVERTISE_10HALF | ADVERTISE_10FULL); -+ local_cap |= (ADVERTISE_100HALF | ADVERTISE_100FULL); -+ local_giga_cap |= (ADVERTISE_1000HALF | ADVERTISE_1000FULL); -+ } else { -+ return -EINVAL; -+ } -+ -+ /* Active PHY with the requested speed for auto-negotiation */ -+ ret = bcm539x_write_16(bd, REG_MII_PAGE + inst, (MII_ADVERTISE << 1), local_cap); -+ if (ret) { -+ return ret; -+ } -+ ret = bcm539x_write_16(bd, REG_MII_PAGE + inst, (MII_CTRL1000 << 1), local_giga_cap); -+ if (ret) { -+ return ret; -+ } -+ -+ ret = bcm539x_read_16(bd, REG_MII_PAGE + inst, (MII_BMCR << 1), &ctrl_word); -+ if (ret) { -+ return ret; -+ } -+ ctrl_word |= (BMCR_ANENABLE | BMCR_ANRESTART); -+ ret = bcm539x_write_16(bd, REG_MII_PAGE + inst, (MII_BMCR << 1), ctrl_word); -+ if (ret) { -+ return ret; -+ } -+ -+ return 0; -+} -+ -+/* -+ * proc_fs entries for this switch -+ */ -+static const struct switch_handler bcm539x_switch_handlers[] = { -+ { -+ .name = "enable", -+ .read = bcm539x_handle_enable_read, -+ .write = bcm539x_handle_enable_write, -+ }, -+ { -+ .name = "enable_vlan", -+ .read = bcm539x_handle_enable_vlan_read, -+ .write = bcm539x_handle_enable_vlan_write, -+ }, -+ { -+ .name = "reset", -+ .write = bcm539x_handle_reset, -+ }, -+ { -+ }, -+}; -+ -+/* -+ * Handlers for /vlan -+ */ -+static const struct switch_handler bcm539x_switch_handlers_vlan[] = { -+ { -+ .name = "delete", -+ .write = bcm539x_handle_vlan_delete_write, -+ }, -+ { -+ .name = "create", -+ .write = bcm539x_handle_vlan_create_write, -+ }, -+ { -+ }, -+}; -+ -+/* -+ * Handlers for /port/ -+ */ -+static const struct switch_handler bcm539x_switch_handlers_port[] = { -+ { -+ .name = "enable", -+ .read = bcm539x_handle_port_enable_read, -+ .write = bcm539x_handle_port_enable_write, -+ }, -+ { -+ .name = "state", -+ .read = bcm539x_handle_port_state_read, -+ }, -+ { -+ .name = "media", -+ .read = bcm539x_handle_port_media_read, -+ .write = bcm539x_handle_port_meida_write, -+ }, -+ { -+ }, -+}; -+ -+/* -+ * bcm539x_probe -+ */ -+static int __devinit bcm539x_probe(struct spi_device *spi) -+{ -+ struct bcm539x_data *bd; -+ struct switch_core_platform_data *pdata; -+ struct switch_device *switch_dev = NULL; -+ int i, ret; -+ u8_t txbuf[2]; -+ -+ pdata = spi->dev.platform_data; -+ if (!pdata) { -+ return -EINVAL; -+ } -+ -+ ret = spi_setup(spi); -+ if (ret < 0) { -+ return ret; -+ } -+ -+ /* -+ * Reset the chip if requested -+ */ -+ if (pdata->flags & SWITCH_DEV_FLAG_HW_RESET) { -+ ret = gpio_request(pdata->pin_reset, "switch-bcm539x-reset"); -+ if (ret) { -+ printk(KERN_WARNING "Could not request reset\n"); -+ return -EINVAL; -+ } -+ -+ gpio_direction_output(pdata->pin_reset, 0); -+ udelay(10); -+ gpio_set_value(pdata->pin_reset, 1); -+ udelay(20); -+ } -+ -+ /* -+ * Allocate our private data structure -+ */ -+ bd = kzalloc(sizeof(struct bcm539x_data), GFP_KERNEL); -+ if (!bd) { -+ return -ENOMEM; -+ } -+ -+ dev_set_drvdata(&spi->dev, bd); -+ bd->pdata = pdata; -+ bd->spi = spi; -+ bd->last_page = 0xFF; -+ -+ /* -+ * First perform SW reset if needed -+ */ -+ if (pdata->flags & SWITCH_DEV_FLAG_SW_RESET) { -+ txbuf[0] = (1 << 7) | (1 << 4); -+ ret = bcm539x_write_bytes(bd, PAGE_PORT_TC, -+ REG_CTRL_SRST, txbuf, 1); -+ if (ret) { -+ goto fail; -+ } -+ -+ udelay(20); -+ -+ txbuf[0] = 0; -+ ret = bcm539x_write_bytes(bd, PAGE_PORT_TC, -+ REG_CTRL_SRST, txbuf, 1); -+ if (ret) { -+ goto fail; -+ } -+ } -+ -+ /* -+ * See if we can see the chip -+ */ -+ for (i = 0; i < 10; i++) { -+ ret = bcm539x_read_bytes(bd, PAGE_MMR, REG_DEVICE_ID, -+ &bd->device_id, 1); -+ if (!ret) { -+ break; -+ } -+ } -+ if (ret) { -+ goto fail; -+ } -+ -+ /* -+ * We only support 5395, 5397, 5398 -+ */ -+ if ((bd->device_id != 0x95) && (bd->device_id != 0x97) && -+ (bd->device_id != 0x98)) { -+ ret = -ENODEV; -+ goto fail; -+ } -+ -+ /* -+ * Override CPU port config: fixed link @1000 with flow control -+ */ -+ ret = bcm539x_read_8(bd, PAGE_PORT_TC, REG_CTRL_MIIPO, txbuf); -+ bcm539x_write_8(bd, PAGE_PORT_TC, REG_CTRL_MIIPO, 0xbb); // Override IMP port config -+ printk("Broadcom SW CPU port setting: 0x%x -> 0xbb\n", txbuf[0]); -+ -+ /* -+ * Setup the switch driver structure -+ */ -+ switch_dev = switch_alloc(); -+ if (!switch_dev) { -+ ret = -ENOMEM; -+ goto fail; -+ } -+ switch_dev->name = pdata->name; -+ -+ switch_dev->ports = (bd->device_id == 0x98) ? 9 : 6; -+ switch_dev->port_mask[0] = (bd->device_id == 0x98) ? 0x1FF : 0x11F; -+ switch_dev->driver_handlers = bcm539x_switch_handlers; -+ switch_dev->reg_handlers = NULL; -+ switch_dev->vlan_handlers = bcm539x_switch_handlers_vlan; -+ switch_dev->port_handlers = bcm539x_switch_handlers_port; -+ -+ bd->switch_dev = switch_dev; -+ switch_set_drvdata(switch_dev, (void *)bd); -+ -+ ret = switch_register(bd->switch_dev); -+ if (ret < 0) { -+ goto fail; -+ } -+ -+ printk(KERN_INFO "bcm53%02x switch chip initialized\n", bd->device_id); -+ -+ return ret; -+ -+fail: -+ if (switch_dev) { -+ switch_release(switch_dev); -+ } -+ dev_set_drvdata(&spi->dev, NULL); -+ kfree(bd); -+ return ret; -+} -+ -+static int __attribute__((unused)) bcm539x_remove(struct spi_device *spi) -+{ -+ struct bcm539x_data *bd; -+ -+ bd = dev_get_drvdata(&spi->dev); -+ -+ if (bd->pdata->flags & SWITCH_DEV_FLAG_HW_RESET) { -+ gpio_free(bd->pdata->pin_reset); -+ } -+ -+ if (bd->switch_dev) { -+ switch_unregister(bd->switch_dev); -+ switch_release(bd->switch_dev); -+ } -+ -+ dev_set_drvdata(&spi->dev, NULL); -+ -+ kfree(bd); -+ -+ return 0; -+} -+ -+static struct spi_driver bcm539x_driver = { -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = bcm539x_probe, -+ .remove = __devexit_p(bcm539x_remove), -+}; -+ -+static int __init bcm539x_init(void) -+{ -+ return spi_register_driver(&bcm539x_driver); -+} -+ -+module_init(bcm539x_init); -+ -+static void __exit bcm539x_exit(void) -+{ -+ spi_unregister_driver(&bcm539x_driver); -+} -+module_exit(bcm539x_exit); -+ -+MODULE_AUTHOR("Pat Tjin"); -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("bcm539x SPI switch chip driver"); ---- /dev/null -+++ b/arch/ubicom32/mach-common/switch-bcm539x-reg.h -@@ -0,0 +1,221 @@ -+/* -+ * arch/ubicom32/mach-common/switch-bcm539x-reg.h -+ * Broadcom switch 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+/* -+ * Broadcom 53xx RoboSwitch device driver. -+ * -+ * Copyright 2007, Broadcom Corporation -+ * All Rights Reserved. -+ * -+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY -+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM -+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS -+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. -+ * -+ * $Id$ -+ */ -+ -+#ifndef _SWITCH_BCM539X_REG_H_ -+#define _SWITCH_BCM539X_REG_H_ -+ -+#define BCM539X_CMD_READ 0x60 -+#define BCM539X_CMD_WRITE 0x61 -+ -+#define BCM539X_GLOBAL_SPI_DATA0 0xf0 -+ -+#define BCM539X_GLOBAL_SPI_STATUS 0xfe -+#define BCM539X_GLOBAL_SPI_ST_SPIF (1<<7) -+#define BCM539X_GLOBAL_SPI_ST_RACK (1<<5) -+ -+#define BCM539X_GLOBAL_PAGE 0xff -+ -+#define PAGE_PORT_TC 0x00 // Port Traffic Control Register -+ -+#define PAGE_QOS_CTL 0x30 // QoS Global Control Register -+#define PAGE_QOS_TAG 0x34 // Default IEEE 802.1Q TAG Register -+ -+#define PAGE_MII_CTL_PORT0 0x10 // Internal PHY MII Register -+#define PAGE_MII_CTL_PORT1 0x11 -+#define PAGE_MII_CTL_PORT2 0x12 -+#define PAGE_MII_CTL_PORT3 0x13 -+#define PAGE_MII_CTL_PORT4 0x14 -+ -+#define PAGE_STATUS 0x01 // Status Register Page -+#define PAGE_RATE_CONTROL 0x41 // Broadcast Storm Suppression Register -+ -+#define REG_GRATE_CONTROL 0x00 -+ -+#define REG_LED_POWER 0x12 -+ -+// Ingress Rate Control -+#define REG_IRATE_CONTROLP0 0x10 -+#define REG_IRATE_CONTROLP1 0x14 -+#define REG_IRATE_CONTROLP2 0x18 -+#define REG_IRATE_CONTROLP3 0x1C -+#define REG_IRATE_CONTROLP4 0x20 -+#define REG_IRATE_CONTROLP7 0x2C -+#define REG_IRATE_CONTROLPI 0x30 -+ -+// Egress Rate Control -+#define REG_ERATE_CONTROLP0 0x80 -+#define REG_ERATE_CONTROLP1 0x82 -+#define REG_ERATE_CONTROLP2 0x84 -+#define REG_ERATE_CONTROLP3 0x86 -+#define REG_ERATE_CONTROLP4 0x88 -+#define REG_ERATE_CONTROLP5 0x8A -+#define REG_ERATE_CONTROLP6 0x8C -+#define REG_ERATE_CONTROLP7 0x8E -+#define REG_ERATE_CONTROLPI 0x90 -+ -+#define REG_LINK_STATUS 0x00 -+ -+#define REG_TC_PORT0 0x00 -+#define REG_TC_PORT1 0x01 -+#define REG_TC_PORT2 0x02 -+#define REG_TC_PORT3 0x03 -+#define REG_TC_PORT4 0x04 -+#define REG_TC_PORT5 0x05 -+ -+#define REG_SPEED_CTL 0x00 -+#define REG_SPEED_ADV100 0x08 -+#define REG_SPEED_ADV1000 0x12 -+ -+#define REG_QOS_EN 0x00 -+#define REG_QOS_TAG_PORT1 0x12 // Default IEEE 802.1Q TAG, PORT 1 -+#define REG_QOS_TAG_PORT2 0x14 // Default IEEE 802.1Q TAG, PORT 2 -+#define REG_QOS_TAG_PORT3 0x16 // Default IEEE 802.1Q TAG, PORT 3 -+#define REG_QOS_TAG_PORT4 0x18 // Default IEEE 802.1Q TAG, PORT 4 -+#define REG_QOS_PID_PORT1 0x52 // Ingress Port Priority ID MAP, PORT 1 -+#define REG_QOS_PID_PORT2 0x54 // Ingress Port Priority ID MAP, PORT 2 -+#define REG_QOS_PID_PORT3 0x56 // Ingress Port Priority ID MAP, PORT 3 -+#define REG_QOS_PID_PORT4 0x58 // Ingress Port Priority ID MAP, PORT 4 -+#define REG_QOS_TXQ_CTL 0x80 // Tx Queue Control Register -+#define REG_QOS_TXQ_WHTQ0 0x81 // Tx Queue Weight Register Queue 0 -+#define REG_QOS_TXQ_WHTQ1 0x82 // Tx Queue Weight Register Queue 1 -+#define REG_QOS_TXQ_WHTQ2 0x83 // Tx Queue Weight Register Queue 2 -+#define REG_QOS_TXQ_WHTQ3 0x84 // Tx Queue Weight Register Queue 3 -+ -+#define REG_CTRL_PPSEL 0x24 /* 5397: Protected port select register */ -+ -+#define RATE_CONTROL_ENABLED (1 << 22) -+#define RATE_CONTROL_BSIZE ((1 << 10) | (1 << 9) | (1 << 8)) -+ -+#define RATE_CONTROL_HIGH ((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4)) -+#define RATE_CONTROL_HIGH_N ~((1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)) -+ -+#define RATE_CONTROL_MEDIUM ((1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)) -+#define RATE_CONTROL_MEDIUM_N ~((1 << 7)) -+ -+#define RATE_CONTROL_NORMAL ((1 << 5) | (1 << 2) | (1 << 0)) -+#define RATE_CONTROL_NORMAL_N ~((1 << 7) | (1 << 6) | (1 << 4) | (1 << 3) | (1 << 1)) -+ -+#define RATE_CONTROL_LOW ((1 << 4) | (1 << 3) | (1 << 0)) -+#define RATE_CONTROL_LOW_N ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2) | (1 << 1)) -+ -+// --- Gemtek, Configure the switch to support Ethernet Port QoS -+ -+/* MII access registers */ -+#define PSEUDO_PHYAD 0x1E /* MII Pseudo PHY address */ -+#define REG_MII_PAGE 0x10 /* MII Page register */ -+#define REG_MII_ADDR 0x11 /* MII Address register */ -+#define REG_MII_DATA0 0x18 /* MII Data register 0 */ -+#define REG_MII_DATA1 0x19 /* MII Data register 1 */ -+#define REG_MII_DATA2 0x1a /* MII Data register 2 */ -+#define REG_MII_DATA3 0x1b /* MII Data register 3 */ -+ -+/* Page numbers */ -+#define PAGE_CTRL 0x00 /* Control page */ -+#define PAGE_MMR 0x02 /* 5397 Management/Mirroring page */ -+#define PAGE_VTBL 0x05 /* ARL/VLAN Table access page */ -+#define PAGE_VLAN 0x34 /* VLAN page */ -+ -+/* Control page registers */ -+#define REG_CTRL_PORT0 0x00 /* Port 0 traffic control register */ -+#define REG_CTRL_PORT1 0x01 /* Port 1 traffic control register */ -+#define REG_CTRL_PORT2 0x02 /* Port 2 traffic control register */ -+#define REG_CTRL_PORT3 0x03 /* Port 3 traffic control register */ -+#define REG_CTRL_PORT4 0x04 /* Port 4 traffic control register */ -+#define REG_CTRL_PORT5 0x05 /* Port 5 traffic control register */ -+#define REG_CTRL_PORT6 0x06 /* Port 6 traffic control register */ -+#define REG_CTRL_PORT7 0x07 /* Port 7 traffic control register */ -+#define REG_CTRL_MODE 0x0B /* Switch Mode register */ -+#define REG_CTRL_MIIPO 0x0E /* 5325: MII Port Override register */ -+#define REG_CTRL_SRST 0x79 /* Software reset control register */ -+ -+#define REG_DEVICE_ID 0x30 /* 539x Device id: */ -+#define DEVID5395 0x95 /* 5395 */ -+#define DEVID5397 0x97 /* 5397 */ -+#define DEVID5398 0x98 /* 5398 */ -+#define REG_REVISION_ID 0x40 /* 539x Revision id: */ -+ -+/* VLAN page registers */ -+#define REG_VLAN_CTRL0 0x00 /* VLAN Control 0 register */ -+#define REG_VLAN_CTRL1 0x01 /* VLAN Control 1 register */ -+#define REG_VLAN_CTRL2 0x02 /* VLAN Control 2 register */ -+#define REG_VLAN_CTRL3 0x03 /* VLAN Control 3 register */ -+#define REG_VLAN_CTRL4 0x04 /* VLAN Control 4 register */ -+#define REG_VLAN_CTRL5 0x05 /* VLAN Control 5 register */ -+#define REG_VLAN_ACCESS 0x06 /* VLAN Table Access register */ -+#define REG_VLAN_WRITE 0x08 /* VLAN Write register */ -+#define REG_VLAN_READ 0x0C /* VLAN Read register */ -+#define REG_VLAN_PTAG0 0x10 /* VLAN Default Port Tag register - port 0 */ -+#define REG_VLAN_PTAG1 0x12 /* VLAN Default Port Tag register - port 1 */ -+#define REG_VLAN_PTAG2 0x14 /* VLAN Default Port Tag register - port 2 */ -+#define REG_VLAN_PTAG3 0x16 /* VLAN Default Port Tag register - port 3 */ -+#define REG_VLAN_PTAG4 0x18 /* VLAN Default Port Tag register - port 4 */ -+#define REG_VLAN_PTAG5 0x1a /* VLAN Default Port Tag register - port 5 */ -+#define REG_VLAN_PTAG6 0x1c /* VLAN Default Port Tag register - port 6 */ -+#define REG_VLAN_PTAG7 0x1e /* VLAN Default Port Tag register - port 7 */ -+#define REG_VLAN_PTAG8 0x20 /* 539x: VLAN Default Port Tag register - IMP port */ -+#define REG_VLAN_PMAP 0x20 /* 5325: VLAN Priority Re-map register */ -+ -+/* ARL/VLAN Table Access page registers */ -+#define REG_VTBL_CTRL 0x00 /* ARL Read/Write Control */ -+#define REG_VTBL_MINDX 0x02 /* MAC Address Index */ -+#define REG_VTBL_VINDX 0x08 /* VID Table Index */ -+#define REG_VTBL_ARL_E0 0x10 /* ARL Entry 0 */ -+#define REG_VTBL_ARL_E1 0x18 /* ARL Entry 1 */ -+#define REG_VTBL_DAT_E0 0x18 /* ARL Table Data Entry 0 */ -+#define REG_VTBL_SCTRL 0x20 /* ARL Search Control */ -+#define REG_VTBL_SADDR 0x22 /* ARL Search Address */ -+#define REG_VTBL_SRES 0x24 /* ARL Search Result */ -+#define REG_VTBL_SREXT 0x2c /* ARL Search Result */ -+#define REG_VTBL_VID_E0 0x30 /* VID Entry 0 */ -+#define REG_VTBL_VID_E1 0x32 /* VID Entry 1 */ -+#define REG_VTBL_PREG 0xFF /* Page Register */ -+#define REG_VTBL_ACCESS 0x60 /* VLAN table access register */ -+#define REG_VTBL_INDX 0x61 /* VLAN table address index register */ -+#define REG_VTBL_ENTRY 0x63 /* VLAN table entry register */ -+#define REG_VTBL_ACCESS_5395 0x80 /* VLAN table access register */ -+#define REG_VTBL_INDX_5395 0x81 /* VLAN table address index register */ -+#define REG_VTBL_ENTRY_5395 0x83 /* VLAN table entry register */ -+ -+/* SPI registers */ -+#define REG_SPI_PAGE 0xff /* SPI Page register */ -+ -+#endif ---- /dev/null -+++ b/arch/ubicom32/mach-common/switch-core.c -@@ -0,0 +1,737 @@ -+/* -+ * arch/ubicom32/mach-common/switch-core.c -+ * Ubicom32 architecture switch and /proc/switch/... implementation. -+ * -+ * (C) Copyright 2009, Ubicom, Inc. -+ * Copyright (C) 2005 Felix Fietkau -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ * -+ * Basic doc of driver's /proc interface: -+ * /proc/switch// -+ * registers: read-only -+ * counters: read-only -+ * reset: write causes hardware reset -+ * enable: "0", "1" -+ * enable_vlan: "0", "1" -+ * port// -+ * enabled: "0", "1" -+ * link state: read-only -+ * media: "AUTO", "1000FD", "100FD", "100HD", "10FD", "10HD" -+ * vlan// -+ * ports: same syntax as for nvram's vlan*ports (eg. "1 2 3 4 5*") -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "switch-core.h" -+ -+/* -+ * Pointer to the root of our filesystem -+ */ -+static struct proc_dir_entry *switch_root; -+ -+/* -+ * Lock used to manage access to the switch list -+ */ -+DECLARE_RWSEM(switch_list_lock); -+EXPORT_SYMBOL_GPL(switch_list_lock); -+ -+/* -+ * List of switches we are managing -+ */ -+LIST_HEAD(switch_list); -+EXPORT_SYMBOL_GPL(switch_list); -+ -+/* -+ * List of handlers we have -+ */ -+LIST_HEAD(switch_handler_list); -+EXPORT_SYMBOL_GPL(switch_handler_list); -+ -+/* -+ * Keep track of all the handlers we added -+ */ -+struct switch_handler_entry { -+ struct list_head node; -+ struct proc_dir_entry *parent; -+ struct switch_device *dev; -+ const struct switch_handler *handler; -+ int inst; -+}; -+ -+/* -+ * Keep track of all VLAN dirs we created -+ */ -+struct switch_vlan_entry { -+ struct list_head node; -+ struct proc_dir_entry *pde; -+ int vlan_id; -+ const struct switch_handler *handlers; -+}; -+ -+/* -+ * switch_parse_vlan_ports -+ * Parse the vlan properties written to /vlan//ports -+ */ -+void switch_parse_vlan_ports(struct switch_device *switch_dev, -+ char *buf, u32_t *untag, -+ u32_t *ports, u32_t *def) -+{ -+ u32_t tag = 0; -+ *untag = 0; -+ *ports = 0; -+ *def = 0; -+ -+ -+ /* -+ * Skip any leading spaces -+ */ -+ while (isspace(*buf)) { -+ buf++; -+ } -+ -+ /* -+ * Parse out the string -+ */ -+ while (*buf) { -+ u32_t port = simple_strtoul(buf, &buf, 10); -+ u32_t mask = (1 << port); -+ -+ /* -+ * Parse out any flags -+ */ -+ while (*buf && !isspace(*buf)) { -+ switch (*buf++) { -+ case 't': -+ tag |= mask; -+ break; -+ case '*': -+ *def |= mask; -+ break; -+ } -+ } -+ *ports |= mask; -+ -+ /* -+ * Skip any spaces -+ */ -+ while (isspace(*buf)) { -+ buf++; -+ } -+ } -+ -+ *untag = ~tag & *ports; -+} -+ -+/* -+ * switch_proc_read -+ * Handle reads from the procfs, dispatches the driver specific handler -+ */ -+static ssize_t switch_proc_read(struct file *file, char *buf, size_t count, -+ loff_t *ppos) -+{ -+ struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode); -+ char *page; -+ int len = 0; -+ -+ page = kmalloc(SWITCH_MAX_BUFSZ, GFP_KERNEL); -+ if (!page) { -+ return -ENOBUFS; -+ } -+ -+ if (pde->data != NULL) { -+ struct switch_handler_entry *she = -+ (struct switch_handler_entry *)pde->data; -+ if (she->handler->read) { -+ len += she->handler->read(she->dev, page + len, -+ she->inst); -+ } -+ } -+ len += 1; -+ -+ if (*ppos < len) { -+ len = min_t(int, len - *ppos, count); -+ if (copy_to_user(buf, (page + *ppos), len)) { -+ kfree(page); -+ return -EFAULT; -+ } -+ *ppos += len; -+ } else { -+ len = 0; -+ } -+ -+ kfree(page); -+ -+ return len; -+} -+ -+/* -+ * switch_proc_write -+ * Handle writes from the procfs, dispatches the driver specific handler -+ */ -+static ssize_t switch_proc_write(struct file *file, const char *buf, -+ size_t count, loff_t *data) -+{ -+ struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode); -+ char *page; -+ int ret = -EINVAL; -+ -+ page = kmalloc(count + 1, GFP_KERNEL); -+ if (page == NULL) -+ return -ENOBUFS; -+ -+ if (copy_from_user(page, buf, count)) { -+ kfree(page); -+ return -EINVAL; -+ } -+ page[count] = 0; -+ -+ if (pde->data != NULL) { -+ struct switch_handler_entry *she = -+ (struct switch_handler_entry *)pde->data; -+ if (she->handler->write) { -+ ret = she->handler->write(she->dev, page, she->inst); -+ if (ret >= 0) { -+ ret = count; -+ } -+ } -+ } -+ -+ kfree(page); -+ return ret; -+} -+ -+/* -+ * File operations for the proc_fs, we must cast here since proc_fs' definitions -+ * differ from file_operations definitions. -+ */ -+static struct file_operations switch_proc_fops = { -+ .read = (ssize_t (*) (struct file *, char __user *, -+ size_t, loff_t *))switch_proc_read, -+ .write = (ssize_t (*) (struct file *, const char __user *, -+ size_t, loff_t *))switch_proc_write, -+}; -+ -+/* -+ * switch_add_handler -+ */ -+static int switch_add_handler(struct switch_device *switch_dev, -+ struct proc_dir_entry *parent, -+ const struct switch_handler *handler, -+ int inst) -+{ -+ struct switch_handler_entry *she; -+ struct proc_dir_entry *pde; -+ int mode; -+ -+ she = (struct switch_handler_entry *) -+ kzalloc(sizeof(struct switch_handler_entry), GFP_KERNEL); -+ if (!she) { -+ return -ENOMEM; -+ } -+ -+ INIT_LIST_HEAD(&she->node); -+ she->parent = parent; -+ she->dev = switch_dev; -+ she->inst = inst; -+ she->handler = handler; -+ list_add(&she->node, &switch_dev->handlers); -+ -+ mode = 0; -+ if (handler->read != NULL) { -+ mode |= S_IRUSR; -+ } -+ if (handler->write != NULL) { -+ mode |= S_IWUSR; -+ } -+ -+ pde = create_proc_entry(handler->name, mode, parent); -+ if (!pde) { -+ kfree(she); -+ printk("Failed to create node '%s' in parent %p\n", -+ handler->name, parent); -+ return -ENOMEM; -+ } -+ pde->data = (void *)she; -+ pde->proc_fops = &switch_proc_fops; -+ -+ return 0; -+} -+ -+/* -+ * switch_add_handlers -+ */ -+static int switch_add_handlers(struct switch_device *switch_dev, -+ struct proc_dir_entry *parent, -+ const struct switch_handler *handlers, -+ int inst) -+{ -+ while (handlers->name) { -+ int ret = switch_add_handler(switch_dev, -+ parent, handlers, inst); -+ if (ret) { -+ return ret; -+ } -+ handlers++; -+ } -+ -+ return 0; -+} -+ -+/* -+ * switch_remove_vlan_dirs -+ * Removes all vlan directories -+ * -+ * Assumes all vlan directories are empty, should be called after -+ * switch_remove_handlers -+ */ -+static void switch_remove_vlan_dirs(struct switch_device *switch_dev) -+{ -+ struct list_head *pos; -+ struct list_head *tmp; -+ struct switch_vlan_entry *sve; -+ -+ list_for_each_safe(pos, tmp, &switch_dev->vlan_dirs) { -+ sve = list_entry(pos, struct switch_vlan_entry, node); -+ list_del(pos); -+ remove_proc_entry(sve->pde->name, switch_dev->vlan_dir); -+ kfree(sve); -+ } -+} -+ -+/* -+ * switch_remove_handlers -+ * Removes all handlers registered to the given switch_device -+ */ -+static void switch_remove_handlers(struct switch_device *switch_dev) -+{ -+ struct list_head *pos; -+ struct list_head *tmp; -+ struct switch_handler_entry *she; -+ -+ list_for_each_safe(pos, tmp, &switch_dev->handlers) { -+ she = list_entry(pos, struct switch_handler_entry, node); -+ list_del(pos); -+ remove_proc_entry(she->handler->name, she->parent); -+ kfree(she); -+ } -+} -+ -+/* -+ * switch_unregister_proc_nodes -+ * Unregisters all proc nodes related to switch_dev -+ */ -+void switch_unregister_proc_nodes(struct switch_device *switch_dev) -+{ -+ switch_remove_handlers(switch_dev); -+ -+ if (switch_dev->port_dirs) { -+ int i; -+ -+ for (i = 0; i < switch_dev->ports; i++) { -+ if (switch_dev->port_dirs[i]) { -+ remove_proc_entry( -+ switch_dev->port_dirs[i]->name, -+ switch_dev->port_dir); -+ } -+ } -+ } -+ -+ if (switch_dev->port_dir) { -+ remove_proc_entry("port", switch_dev->driver_dir); -+ switch_dev->port_dir = NULL; -+ } -+ -+ if (switch_dev->reg_dir) { -+ remove_proc_entry("reg", switch_dev->reg_dir); -+ switch_dev->reg_dir = NULL; -+ } -+ -+ if (switch_dev->vlan_dir) { -+ switch_remove_vlan_dirs(switch_dev); -+ remove_proc_entry("vlan", switch_dev->driver_dir); -+ switch_dev->vlan_dir = NULL; -+ } -+ -+ if (switch_dev->driver_dir) { -+ remove_proc_entry(switch_dev->name, switch_root); -+ switch_dev->driver_dir = NULL; -+ } -+} -+ -+/* -+ * switch_remove_vlan_dir -+ * Removes vlan dir in switch//vlan/ -+ */ -+int switch_remove_vlan_dir(struct switch_device *switch_dev, int vlan_id) -+{ -+ struct list_head *pos; -+ struct switch_vlan_entry *sve = NULL; -+ -+ list_for_each(pos, &switch_dev->vlan_dirs) { -+ struct switch_vlan_entry *tmp = -+ list_entry(pos, struct switch_vlan_entry, node); -+ if (tmp->vlan_id == vlan_id) { -+ sve = tmp; -+ break; -+ } -+ } -+ -+ if (!sve) { -+ return -ENOENT; -+ } -+ -+ /* -+ * Remove it from the list -+ */ -+ list_del(pos); -+ -+ /* -+ * Remove the handlers -+ */ -+ while (sve->handlers->name) { -+ remove_proc_entry(sve->handlers->name, sve->pde); -+ sve->handlers++; -+ } -+ -+ /* -+ * Remove the proc entry for the dir -+ */ -+ remove_proc_entry(sve->pde->name, switch_dev->vlan_dir); -+ -+ kfree(sve); -+ -+ return 0; -+} -+ -+/* -+ * switch_create_vlan_dir -+ * Creates vlan dir in switch//vlan/ -+ */ -+int switch_create_vlan_dir(struct switch_device *switch_dev, -+ int vlan_id, const struct switch_handler *handlers) -+{ -+ char s[14]; -+ struct proc_dir_entry *pde = NULL; -+ struct switch_vlan_entry *sve = NULL; -+ int ret; -+ struct list_head *pos; -+ -+ /* -+ * Check to see if it exists already -+ */ -+ list_for_each(pos, &switch_dev->vlan_dirs) { -+ sve = list_entry(pos, struct switch_vlan_entry, node); -+ if (sve->vlan_id == vlan_id) { -+ return -EEXIST; -+ } -+ } -+ sve = NULL; -+ -+ /* -+ * Create the vlan directory if we didn't have it before -+ */ -+ if (!switch_dev->vlan_dir) { -+ switch_dev->vlan_dir = proc_mkdir("vlan", -+ switch_dev->driver_dir); -+ if (!switch_dev->vlan_dir) { -+ goto fail; -+ } -+ if (switch_dev->vlan_handlers) { -+ ret = switch_add_handlers(switch_dev, -+ switch_dev->vlan_dir, -+ switch_dev->vlan_handlers, 0); -+ if (ret) { -+ goto fail; -+ } -+ } -+ } -+ -+ /* -+ * Create the vlan_id directory -+ */ -+ snprintf(s, 14, "%d", vlan_id); -+ pde = proc_mkdir(s, switch_dev->vlan_dir); -+ if (!pde) { -+ goto fail; -+ } -+ -+ /* -+ * Create the handlers for this vlan -+ */ -+ if (handlers) { -+ ret = switch_add_handlers(switch_dev, pde, handlers, vlan_id); -+ if (ret) { -+ goto fail; -+ } -+ } -+ -+ /* -+ * Keep track of all the switch vlan entries created -+ */ -+ sve = (struct switch_vlan_entry *) -+ kzalloc(sizeof(struct switch_vlan_entry), GFP_KERNEL); -+ if (!sve) { -+ goto fail; -+ } -+ INIT_LIST_HEAD(&sve->node); -+ sve->handlers = handlers; -+ sve->vlan_id = vlan_id; -+ sve->pde = pde; -+ list_add(&sve->node, &switch_dev->vlan_dirs); -+ -+ return 0; -+ -+fail: -+ if (sve) { -+ kfree(sve); -+ } -+ -+ if (pde) { -+ /* -+ * Remove any proc entries we might have created -+ */ -+ while (handlers->name) { -+ remove_proc_entry(handlers->name, pde); -+ handlers++; -+ } -+ -+ remove_proc_entry(s, switch_dev->driver_dir); -+ } -+ -+ return -ENOMEM; -+} -+ -+/* -+ * switch_register_proc_nodes -+ */ -+int switch_register_proc_nodes(struct switch_device *switch_dev) -+{ -+ int i; -+ int n; -+ -+ switch_dev->port_dirs = kzalloc(switch_dev->ports * -+ sizeof(struct proc_dir_entry *), -+ GFP_KERNEL); -+ if (!switch_dev->port_dirs) { -+ return -ENOMEM; -+ } -+ -+ /* -+ * Create a new proc entry for this switch -+ */ -+ switch_dev->driver_dir = proc_mkdir(switch_dev->name, switch_root); -+ if (!switch_dev->driver_dir) { -+ goto fail; -+ } -+ if (switch_dev->driver_handlers) { -+ switch_add_handlers(switch_dev, -+ switch_dev->driver_dir, -+ switch_dev->driver_handlers, -+ 0); -+ } -+ -+ /* -+ * Create the ports -+ */ -+ switch_dev->port_dir = proc_mkdir("port", switch_dev->driver_dir); -+ if (!switch_dev->port_dir) { -+ goto fail; -+ } -+ for (n = 0, i = 0; i < (SWITCH_PORT_MASK_SIZE * 32); i++) { -+ if (switch_dev->port_mask[i / 32] & (1 << i % 32)) { -+ char s[14]; -+ -+ snprintf(s, 14, "%d", i); -+ switch_dev->port_dirs[n] = -+ proc_mkdir(s, switch_dev->port_dir); -+ if (!switch_dev->port_dirs[n]) { -+ goto fail; -+ } -+ if (switch_dev->port_handlers) { -+ switch_add_handlers(switch_dev, -+ switch_dev->port_dirs[n], -+ switch_dev->port_handlers, -+ i); -+ } -+ n++; -+ } -+ } -+ -+ /* -+ * Create the register directory for switch register access. -+ */ -+ if (switch_dev->reg_handlers) { -+ switch_dev->reg_dir = proc_mkdir("reg", switch_dev->driver_dir); -+ if (!switch_dev->reg_dir) { -+ goto fail; -+ } -+ -+ switch_add_handlers(switch_dev, -+ switch_dev->reg_dir, -+ switch_dev->reg_handlers, -+ 0); -+ } -+ -+ /* -+ * Create the vlan directory -+ */ -+ if (switch_dev->vlan_handlers) { -+ switch_dev->vlan_dir = proc_mkdir("vlan", -+ switch_dev->driver_dir); -+ if (!switch_dev->vlan_dir) { -+ goto fail; -+ } -+ if (switch_dev->vlan_handlers) { -+ switch_add_handlers(switch_dev, -+ switch_dev->vlan_dir, -+ switch_dev->vlan_handlers, -+ 0); -+ } -+ } -+ -+ return 0; -+ -+fail: -+ switch_unregister_proc_nodes(switch_dev); -+ return -ENOMEM; -+} -+ -+/* -+ * switch_release -+ */ -+void switch_release(struct switch_device *switch_dev) -+{ -+ kfree(switch_dev); -+} -+ -+/* -+ * switch_alloc -+ */ -+struct switch_device *switch_alloc(void) -+{ -+ struct switch_device *switch_dev = -+ kzalloc(sizeof(struct switch_device), -+ GFP_KERNEL); -+ INIT_LIST_HEAD(&switch_dev->node); -+ INIT_LIST_HEAD(&switch_dev->vlan_dirs); -+ INIT_LIST_HEAD(&switch_dev->handlers); -+ return switch_dev; -+} -+ -+/* -+ * switch_register -+ */ -+int switch_register(struct switch_device *switch_dev) -+{ -+ int ret; -+ int i; -+ -+ /* -+ * Make sure that the number of ports and the port mask make sense -+ */ -+ for (ret = 0, i = 0; i < (SWITCH_PORT_MASK_SIZE * 32); i++) { -+ if (switch_dev->port_mask[i / 32] & (1 << i % 32)) { -+ ret++; -+ } -+ } -+ if (ret > switch_dev->ports) { -+ return -EINVAL; -+ } -+ -+ /* -+ * Create the /proc entries -+ */ -+ ret = switch_register_proc_nodes(switch_dev); -+ if (ret) { -+ return ret; -+ } -+ -+ /* -+ * Add it to the list of switches -+ */ -+ down_write(&switch_list_lock); -+ list_add_tail(&switch_dev->node, &switch_list); -+ up_write(&switch_list_lock); -+ -+ printk(KERN_INFO "Registered switch device: %s\n", switch_dev->name); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(switch_register); -+ -+/* -+ * switch_unregister -+ * Unregisters a previously registered switch_device object -+ */ -+void switch_unregister(struct switch_device *switch_dev) -+{ -+ /* -+ * remove the proc entries -+ */ -+ switch_unregister_proc_nodes(switch_dev); -+ -+ /* -+ * Remove it from the list of switches -+ */ -+ down_write(&switch_list_lock); -+ list_del(&switch_dev->node); -+ up_write(&switch_list_lock); -+ -+ printk(KERN_INFO "Unregistered switch device: %s\n", switch_dev->name); -+} -+EXPORT_SYMBOL_GPL(switch_unregister); -+ -+/* -+ * switch_init -+ */ -+static int __init switch_init(void) -+{ -+ switch_root = proc_mkdir("switch", NULL); -+ if (!switch_root) { -+ printk(KERN_WARNING "Failed to make root switch node\n"); -+ return -ENODEV; -+ } -+ return 0; -+} -+module_init(switch_init); -+ -+/* -+ * switch_exit -+ */ -+static void __exit switch_exit(void) -+{ -+ remove_proc_entry("switch", NULL); -+} -+module_exit(switch_exit); -+ -+MODULE_AUTHOR("Patrick Tjin"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Ethernet Switch Class Interface"); ---- /dev/null -+++ b/arch/ubicom32/mach-common/switch-core.h -@@ -0,0 +1,92 @@ -+/* -+ * arch/ubicom32/mach-common/switch-core.h -+ * Private data for the switch module -+ * -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _SWITCH_CORE_H_ -+#define _SWITCH_CORE_H_ -+ -+struct switch_handler_entry; -+struct switch_vlan_entry; -+ -+#define SWITCH_PORT_MASK_SIZE 2 -+ -+struct switch_device { -+ struct list_head node; -+ -+ const char *name; -+ void *drvdata; -+ -+ u8_t ports; -+ -+ struct proc_dir_entry *driver_dir; -+ const struct switch_handler *driver_handlers; -+ -+ struct proc_dir_entry *port_dir; -+ struct proc_dir_entry **port_dirs; -+ const struct switch_handler *port_handlers; -+ -+ struct proc_dir_entry *reg_dir; -+ const struct switch_handler *reg_handlers; -+ -+ struct proc_dir_entry *vlan_dir; -+ const struct switch_handler *vlan_handlers; -+ struct list_head vlan_dirs; -+ -+ struct list_head handlers; -+ -+ u32_t port_mask[SWITCH_PORT_MASK_SIZE]; -+}; -+ -+typedef int (*switch_handler_fn)(struct switch_device *, char *buf, int nr); -+struct switch_handler { -+ const char *name; -+ -+ switch_handler_fn read; -+ switch_handler_fn write; -+}; -+ -+#define SWITCH_MAX_BUFSZ 4096 -+ -+static inline void switch_set_drvdata(struct switch_device *switch_dev, void *drvdata) -+{ -+ switch_dev->drvdata = drvdata; -+} -+ -+static inline void *switch_get_drvdata(struct switch_device *switch_dev) -+{ -+ return switch_dev->drvdata; -+} -+ -+extern int switch_create_vlan_dir(struct switch_device *switch_dev, int vlan_id, const struct switch_handler *handlers); -+extern int switch_remove_vlan_dir(struct switch_device *switch_dev, int vlan_id); -+extern void switch_parse_vlan_ports(struct switch_device *switch_dev, char *buf, u32_t *untag, u32_t *ports, u32_t *def); -+ -+extern void switch_release(struct switch_device *switch_dev); -+extern struct switch_device *switch_alloc(void); -+extern int switch_register(struct switch_device *switch_dev); -+extern void switch_unregister(struct switch_device *switch_dev); -+ -+#endif ---- /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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if defined(CONFIG_PROC_FS) -+#include -+#endif -+ -+#include -+#include -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#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 . -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+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 "); -+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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#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 "); -+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,132 @@ -+/* -+ * arch/ubicom32/mach-common/ip5k_usb.c -+ * Ubicom32 architecture usb support. -+ * -+ * (C) Copyright 2009, Ubicom, Inc. -+ * Copyright (C) 2007 MontaVista Software, Inc. -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+/* -+ * 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,109 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+#include -+#include -+#ifdef CONFIG_SERIAL_UBI32_SERDES -+#include -+#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,75 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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,279 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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,28 @@ -+ -+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 -+ help -+ IP5170 Digital Picture Frame board, 8005-1113, IP5K-BEV-0011-13 v1.3 -+ -+config IP5160DEV -+ bool "IP5160Dev_Ver1Dot1" -+ select UBICOM32_V3 -+ help -+ Ubicom StreamEngine 5000 Development Board, IP5K-BDV-0004-11 v1.1 -+ -+config IP5160EVAL -+ bool "IP5160RGWEval_Ver2Rev2" -+ select UBICOM32_V3 -+ help -+ Ubicom StreamEngine 5000 RGW Evaluation Board, IP5K-RGW-0004-11 v2.2 ---- /dev/null -+++ b/arch/ubicom32/mach-ip5k/Makefile -@@ -0,0 +1,31 @@ -+# -+# 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 . -+# -+# 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,715 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+/****************************************************************************** -+ * 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 -+#include -+#include -+#include -+ -+#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 ubicom32lcdpower_platform_data ip7145dpf_lcdpower_data = { -+ .vgh_gpio = GPIO_RE_6, -+ .vgh_polarity = true, -+}; -+ -+static struct platform_device ip7145dpf_lcdpower_device = { -+ .name = "ubicom32lcdpower", -+ .id = -1, -+ .dev = { -+ .platform_data = &ip7145dpf_lcdpower_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_lcdpower_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_device_alloc("snd-ubi32-generic", "audio", "audio-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,134 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#ifdef CONFIG_SERIAL_UBI32_SERDES -+#include -+#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,326 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * 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, 1); -+ 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_device_alloc("snd-ubi32-cs4350", "audio-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,355 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#ifdef CONFIG_SERIAL_UBI32_SERDES -+#include -+#endif -+ -+#include -+#include -+ -+#ifdef CONFIG_IP7160RGWLCD -+#include -+#include -+/* -+ * LCD Adapter board 8007-092x support -+ * -+ * Touch controller -+ * -+ * Connected via I2C bus, interrupt on PA6 -+ */ -+#include -+ -+/* -+ * ip7160rgwlcd_tsc2007_exit_platform_hw -+ */ -+static void ip7160rgwlcd_tsc2007_exit_platform_hw(void) -+{ -+ UBICOM32_IO_PORT(RA)->ctl0 &= ~(0x03 << 17); -+ gpio_free(GPIO_RA_5); -+} -+ -+/* -+ * ip7160rgwlcd_tsc2007_init_platform_hw -+ */ -+static int ip7160rgwlcd_tsc2007_init_platform_hw(void) -+{ -+ int res = gpio_request(GPIO_RA_5, "TSC2007_IRQ"); -+ if (res) { -+ return res; -+ } -+ -+ UBICOM32_IO_PORT(RA)->ctl0 &= ~(0x03 << 17); -+ UBICOM32_IO_PORT(RA)->ctl0 |= (0x02 << 17); -+ return 0; -+} -+ -+/* -+ * ip7160rgwlcd_tsc2007_get_pendown_state -+ */ -+static int ip7160rgwlcd_tsc2007_get_pendown_state(void) -+{ -+ return !gpio_get_value(GPIO_RA_5); -+} -+ -+static struct tsc2007_platform_data ip7160rgwlcd_tsc2007_data = { -+ .model = 2007, -+ .x_plate_ohms = 350, -+ .get_pendown_state = ip7160rgwlcd_tsc2007_get_pendown_state, -+ .init_platform_hw = ip7160rgwlcd_tsc2007_init_platform_hw, -+ .exit_platform_hw = ip7160rgwlcd_tsc2007_exit_platform_hw, -+}; -+ -+/****************************************************************************** -+ * I2C bus on the board, SDA PI14, SCL PI13 -+ */ -+static struct i2c_gpio_platform_data ip7160rgwlcd_i2c_data = { -+ .sda_pin = GPIO_RI_14, -+ .scl_pin = GPIO_RI_13, -+ .sda_is_open_drain = 0, -+ .scl_is_open_drain = 0, -+ .udelay = 50, -+}; -+ -+static struct platform_device ip7160rgwlcd_i2c_device = { -+ .name = "i2c-gpio", -+ .id = 0, -+ .dev = { -+ .platform_data = &ip7160rgwlcd_i2c_data, -+ }, -+}; -+ -+static struct i2c_board_info __initdata ip7160rgwlcd_i2c_board_info[] = { -+ { -+ .type = "tsc2007", -+ .addr = 0x48, -+ .irq = 45, // RA5 -+ .platform_data = &ip7160rgwlcd_tsc2007_data, -+ }, -+}; -+ -+#endif -+ -+/* -+ * 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_core_platform_data ip7160rgw_bcm539x_platform_data = { -+ .flags = SWITCH_DEV_FLAG_HW_RESET, -+ .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, -+#ifdef CONFIG_IP7160RGWLCD -+ &ip7160rgwlcd_i2c_device, -+#endif -+}; -+ -+/* -+ * 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(); -+ -+ /* -+ * Reserve switch SPI CS on behalf on switch driver -+ */ -+ if (gpio_request(ip7160rgw_bcm539x_controller_data.pin_cs, "switch-bcm539x-cs")) { -+ printk(KERN_WARNING "Could not request cs of switch SPI I/F\n"); -+ return -EIO; -+ } -+ gpio_direction_output(ip7160rgw_bcm539x_controller_data.pin_cs, 1); -+ -+ 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)); -+ -+#ifdef CONFIG_IP7160RGWLCD -+ printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); -+ i2c_register_board_info(0, ip7160rgwlcd_i2c_board_info, ARRAY_SIZE(ip7160rgwlcd_i2c_board_info)); -+ printk(KERN_INFO "IP7160 RGW + LCD\n"); -+#else -+ printk(KERN_INFO "IP7160 RGW\n"); -+#endif -+ return 0; -+} -+ -+arch_initcall(ip7160rgw_init); ---- /dev/null -+++ b/arch/ubicom32/mach-ip7k/board-ip7500av.c -@@ -0,0 +1,273 @@ -+/* -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/****************************************************************************** -+ * 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_vdac_write -+ */ -+static int __init ip7500av_vdac_write(int reg, int val) -+{ -+ struct i2c_adapter *adap; -+ struct i2c_msg msg[1]; -+ unsigned char data[2]; -+ int err; -+ -+ adap = i2c_get_adapter(0); -+ if (!adap) { -+ printk(KERN_WARNING "%s: failed to get i2c adapter\n", __FUNCTION__); -+ return -ENODEV; -+ } -+ msg->addr = 0x2B; -+ msg->flags = 0; -+ msg->len = 2; -+ msg->buf = data; -+ data[0] = reg; -+ data[1] = val; -+ err = i2c_transfer(adap, msg, 1); -+ i2c_put_adapter(adap); -+ if (err >= 0) { -+ return 0; -+ } -+ return err; -+} -+ -+/* -+ * ip7500av_vdac_init -+ * Initializes the video DAC via I2C -+ * -+ * Equivalent mode line: 720x480p = 27 Mhz, 720 736 800 858 480 484 492 525 -+ */ -+static int __init ip7500av_vdac_init(void) -+{ -+ int err; -+ -+ printk(KERN_INFO "Initializing ADV7393 DAC\n"); -+ -+ /* -+ * Reset the VDAC -+ */ -+ if (gpio_request(GPIO_RF_6, "VDAC Reset")) { -+ printk(KERN_WARNING "%s: failed to allocate VDAC Reset\n", __FUNCTION__); -+ return -EBUSY; -+ } -+ gpio_direction_output(GPIO_RF_6, 0); -+ udelay(1); -+ gpio_set_value(GPIO_RF_6, 1); -+ -+ /* -+ * See table 100 of ADV7393 data sheet: 16-bit 525p YCrCb In, YPbPr Out -+ */ -+ err = ip7500av_vdac_write(0x17, 0x02); -+ if (err) { -+ printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); -+ return err; -+ } -+ err = ip7500av_vdac_write(0x00, 0x1c); -+ if (err) { -+ printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); -+ return err; -+ } -+ err = ip7500av_vdac_write(0x01, 0x10); -+ if (err) { -+ printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); -+ return err; -+ } -+ err = ip7500av_vdac_write(0x31, 0x01); -+ if (err) { -+ printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); -+ return err; -+ } -+#ifdef IP7500AV_VDAC_SWAP_PBPR -+ err = ip7500av_vdac_write(0x35, 0x08); -+ if (err) { -+ printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); -+ return err; -+ } -+#endif -+#ifdef IP7500AV_VDAC_FULL_RANGE -+ err = ip7500av_vdac_write(0x30, 0x02); -+ if (err) { -+ printk(KERN_WARNING "%s: failed to write VDAC\n", __FUNCTION__); -+ return err; -+ } -+#endif -+ return 0; -+} -+late_initcall(ip7500av_vdac_init); -+ -+/* -+ * 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(); -+ -+ ubi_gpio_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_device_alloc("snd-ubi32-cs4384", "audio", "audio-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_device_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; -+ -+ /* -+ * Reset the DAC -+ */ -+ if (gpio_request(GPIO_RF_4, "DAC Reset") == 0) { -+ gpio_direction_output(GPIO_RF_4, 0); -+ udelay(1); -+ gpio_direction_output(GPIO_RF_4, 1); -+ } else { -+ printk("Unable to request DAC reset GPIO\n"); -+ } -+ } -+ -+ /* -+ * SPDIF port -+ */ -+ audio_dev2 = audio_device_alloc("snd-ubi32-generic", "audio", "audio-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)); -+ -+ printk(KERN_INFO "IP7500 Audio/Video Board\n"); -+ return 0; -+} -+arch_initcall(ip7500av_init); ---- /dev/null -+++ b/arch/ubicom32/mach-ip7k/board-ip7500iap.c -@@ -0,0 +1,414 @@ -+/* -+ * arch/ubicom32/mach-ip7k/board-ip7500iap.c -+ * Support for IP7500 Internet Audio Player -+ * -+ * This file supports the IP7500 Internet Audio Player: -+ * 8007-1110 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 . -+ */ -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+ -+/****************************************************************************** -+ * SD/IO Port F (Slot 1) platform data -+ */ -+static struct resource ip7500iap_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 ip7500iap_portf_sd_cards[] = { -+ [0] = { -+ .pin_wp = GPIO_RF_7, -+ .wp_polarity = 1, -+ .pin_pwr = GPIO_RF_8, -+ .pin_cd = GPIO_RF_6, -+ }, -+}; -+ -+static struct ubicom32sd_platform_data ip7500iap_portf_sd_platform_data = { -+ .ncards = 1, -+ .cards = ip7500iap_portf_sd_cards, -+}; -+ -+static struct platform_device ip7500iap_portf_sd_device = { -+ .name = "ubicom32sd", -+ .id = 0, -+ .resource = ip7500iap_portf_sd_resources, -+ .num_resources = ARRAY_SIZE(ip7500iap_portf_sd_resources), -+ .dev = { -+ .platform_data = &ip7500iap_portf_sd_platform_data, -+ }, -+ -+}; -+ -+/* -+ * ip7500iap_portf_sd_init -+ */ -+static void ip7500iap_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 -+ */ -+ ip7500iap_portf_sd_resources[0].start = sd_node->dn.sendirq; -+ ip7500iap_portf_sd_resources[1].start = sd_node->dn.recvirq; -+ ip7500iap_portf_sd_resources[2].start = (u32_t)&(sd_node->regs); -+ ip7500iap_portf_sd_resources[2].end = (u32_t)&(sd_node->regs) + sizeof(sd_node->regs); -+ -+ platform_device_register(&ip7500iap_portf_sd_device); -+} -+ -+/****************************************************************************** -+ * SD/IO Port B (Slot 2) platform data -+ */ -+static struct resource ip7500iap_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 ip7500iap_portb_sd_cards[] = { -+ [0] = { -+ .pin_wp = GPIO_RB_13, -+ .wp_polarity = 1, -+ .pin_pwr = GPIO_RB_11, -+ .pin_cd = GPIO_RB_12, -+ }, -+}; -+ -+static struct ubicom32sd_platform_data ip7500iap_portb_sd_platform_data = { -+ .ncards = 1, -+ .cards = ip7500iap_portb_sd_cards, -+}; -+ -+static struct platform_device ip7500iap_portb_sd_device = { -+ .name = "ubicom32sd", -+ .id = 1, -+ .resource = ip7500iap_portb_sd_resources, -+ .num_resources = ARRAY_SIZE(ip7500iap_portb_sd_resources), -+ .dev = { -+ .platform_data = &ip7500iap_portb_sd_platform_data, -+ }, -+ -+}; -+ -+/* -+ * ip7500iap_portb_sd_init -+ */ -+static void ip7500iap_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 -+ */ -+ ip7500iap_portb_sd_resources[0].start = sd_node->dn.sendirq; -+ ip7500iap_portb_sd_resources[1].start = sd_node->dn.recvirq; -+ ip7500iap_portb_sd_resources[2].start = (u32_t)&(sd_node->regs); -+ ip7500iap_portb_sd_resources[2].end = (u32_t)&(sd_node->regs) + sizeof(sd_node->regs); -+ -+ platform_device_register(&ip7500iap_portb_sd_device); -+} -+ -+/****************************************************************************** -+ * Touch controller -+ * -+ * Connected via I2C bus, interrupt on PA6 -+ */ -+#include -+ -+/* -+ * ip7500iap_tsc2007_exit_platform_hw -+ */ -+static void ip7500iap_tsc2007_exit_platform_hw(void) -+{ -+ UBICOM32_IO_PORT(RA)->ctl0 &= ~(0x03 << 19); -+ gpio_free(GPIO_RA_6); -+} -+ -+/* -+ * ip7500iap_tsc2007_init_platform_hw -+ */ -+static int ip7500iap_tsc2007_init_platform_hw(void) -+{ -+ int res = gpio_request(GPIO_RA_6, "TSC2007_IRQ"); -+ if (res) { -+ return res; -+ } -+ -+ UBICOM32_IO_PORT(RA)->ctl0 &= ~(0x03 << 19); -+ UBICOM32_IO_PORT(RA)->ctl0 |= (0x02 << 19); -+ return 0; -+} -+ -+/* -+ * ip7500iap_tsc2007_get_pendown_state -+ */ -+static int ip7500iap_tsc2007_get_pendown_state(void) -+{ -+ return !gpio_get_value(GPIO_RA_6); -+} -+ -+static struct tsc2007_platform_data ip7500iap_tsc2007_data = { -+ .model = 2007, -+ .x_plate_ohms = 350, -+ .get_pendown_state = ip7500iap_tsc2007_get_pendown_state, -+ .init_platform_hw = ip7500iap_tsc2007_init_platform_hw, -+ .exit_platform_hw = ip7500iap_tsc2007_exit_platform_hw, -+}; -+ -+/****************************************************************************** -+ * i2c devices -+ * -+ * DO NOT CHANGE THE ORDER HERE unless you know how this works. There -+ * are hardcoded indicies which refer to the order of drivers listed here. -+ */ -+static struct i2c_board_info __initdata ip7500iap_i2c_board_info[] = { -+ /* -+ * U6, CS4350 DAC, address 0x4B -+ */ -+ { -+ .type = "cs4350", -+ .addr = 0x4B, -+ }, -+ -+ /* -+ * U20, S35390A RTC, address 0x30 -+ */ -+ { -+ .type = "s35390a", -+ .addr = 0x30, -+ }, -+ -+ /* -+ * U9, TSC2007 Touch screen controller, address 0x49, irq RA6 -+ */ -+ { -+ .type = "tsc2007", -+ .addr = 0x49, -+ .irq = 46, -+ .platform_data = &ip7500iap_tsc2007_data, -+ }, -+}; -+ -+/* -+ * I2C bus on the board, SDA PE4, SCL PE5 -+ */ -+static struct i2c_gpio_platform_data ip7500iap_i2c_data = { -+ .sda_pin = GPIO_RF_14, -+ .scl_pin = GPIO_RF_13, -+ .sda_is_open_drain = 0, -+ .scl_is_open_drain = 0, -+ .udelay = 50, -+}; -+ -+static struct platform_device ip7500iap_i2c_device = { -+ .name = "i2c-gpio", -+ .id = 0, -+ .dev = { -+ .platform_data = &ip7500iap_i2c_data, -+ }, -+}; -+ -+/****************************************************************************** -+ * Backlight on the board PD0, hardware PWM -+ */ -+static struct ubicom32bl_platform_data ip7500iap_backlight_data = { -+ .type = UBICOM32BL_TYPE_PWM, -+ .pwm_channel = 2, -+ .pwm_prescale = 15, -+ .pwm_period = 60, -+ .default_intensity = 0x80, -+}; -+ -+static struct platform_device ip7500iap_backlight_device = { -+ .name = "ubicom32bl", -+ .id = -1, -+ .dev = { -+ .platform_data = &ip7500iap_backlight_data, -+ }, -+}; -+ -+/****************************************************************************** -+ * Devices on this board -+ */ -+static struct platform_device *ip7500iap_devices[] __initdata = { -+ &ip7500iap_i2c_device, -+ &ip7500iap_backlight_device, -+}; -+ -+/* -+ * ip7500iap_power_off -+ * Called to turn the power off for this board -+ */ -+static void ip7500iap_power_off(void) -+{ -+ gpio_set_value(GPIO_RF_11, 0); -+} -+ -+/* -+ * ip7500iap_init -+ * Called to add the devices which we have on this board -+ */ -+static int __init ip7500iap_init(void) -+{ -+ struct platform_device *audio_dev; -+ struct platform_device *audio_dev2; -+ int ret; -+ -+ board_init(); -+ -+ ubi_gpio_init(); -+ -+ /* -+ * Hold the POWER_HOLD line -+ */ -+ ret = gpio_request(GPIO_RF_11, "POWER_HOLD"); -+ if (ret) { -+ printk(KERN_ERR "%s: could not request POWER_HOLD GPIO\n", __FUNCTION__); -+ } -+ gpio_direction_output(GPIO_RF_11, 1); -+ mach_power_off = ip7500iap_power_off; -+ -+ /* -+ * DAC nRESET line -+ */ -+ ret = gpio_request(GPIO_RE_7, "DAC_nRESET"); -+ if (ret) { -+ printk(KERN_ERR "%s: could not request DAC_nRESET GPIO\n", __FUNCTION__); -+ } -+ gpio_direction_output(GPIO_RE_7, 0); -+ udelay(1); -+ gpio_set_value(GPIO_RE_7, 1); -+ -+ /* -+ * Bring up any SDIO slots -+ */ -+ ip7500iap_portb_sd_init(); -+ ip7500iap_portf_sd_init(); -+ -+ /* -+ * Bring up audio devices -+ */ -+ platform_add_devices(ip7500iap_devices, ARRAY_SIZE(ip7500iap_devices)); -+ -+ audio_dev = audio_device_alloc("snd-ubi32-cs4350", "audio", "audio-i2sout", 0); -+ if (audio_dev) { -+ ip7500iap_i2c_board_info[0].platform_data = audio_dev; -+ } -+ -+ audio_dev2 = audio_device_alloc("snd-ubi32-generic", "audio", "audio-spdifout", 0); -+ if (audio_dev2) { -+ platform_device_register(audio_dev2); -+ } -+ -+ printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); -+ i2c_register_board_info(0, ip7500iap_i2c_board_info, ARRAY_SIZE(ip7500iap_i2c_board_info)); -+ -+ printk(KERN_INFO "IP7500 Internet Audio Player\n"); -+ -+ return 0; -+} -+ -+arch_initcall(ip7500iap_init); ---- /dev/null -+++ b/arch/ubicom32/mach-ip7k/board-ip7500media.c -@@ -0,0 +1,732 @@ -+/* -+ * 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 NOPHY -+ * P/N 8007-0511 rev 1.1 NOPHY -+ * 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 -+ * -+ * LCD Adapter Board: (optional) -+ * P/N 8007-0920 rev 2.0 -+ * P/N 8007-0921 rev 2.1 -+ * -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+/****************************************************************************** -+ * 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 ubicom32lcdpower_platform_data ip7500media_lcdpower_data = { -+ .vgh_gpio = GPIO_RE_7, -+ .vgh_polarity = true, -+}; -+ -+static struct platform_device ip7500media_lcdpower_device = { -+ .name = "ubicom32lcdpower", -+ .id = -1, -+ .dev = { -+ .platform_data = &ip7500media_lcdpower_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, -+}; -+ -+ -+/****************************************************************************** -+ * Touch controller present on LCD Adapter board -+ * -+ * Connected via I2C bus, interrupt on PD1 -+ */ -+#include -+ -+/* -+ * ip7500media_tsc2007_exit_platform_hw -+ */ -+static void ip7500media_tsc2007_exit_platform_hw(void) -+{ -+ UBICOM32_IO_PORT(RD)->int_mask &= ~(1 << 11); -+ UBICOM32_IO_PORT(RD)->ctl2 &= ~(0x03 << 16); -+ gpio_free(GPIO_RD_1); -+} -+ -+/* -+ * ip7500media_tsc2007_init_platform_hw -+ */ -+static int ip7500media_tsc2007_init_platform_hw(void) -+{ -+ int res = gpio_request(GPIO_RD_1, "TSC2007_IRQ"); -+ if (res) { -+ return res; -+ } -+ UBICOM32_IO_PORT(RD)->function = 0; -+ UBICOM32_IO_PORT(RD)->int_mask = (1 << 11); -+ UBICOM32_IO_PORT(RD)->ctl2 &= ~(0x03 << 16); -+ UBICOM32_IO_PORT(RD)->ctl2 |= (0x02 << 16); -+ -+ return 0; -+} -+ -+/* -+ * ip7500media_tsc2007_clear_penirq -+ */ -+static void ip7500media_tsc2007_clear_penirq(void) -+{ -+ UBICOM32_IO_PORT(RD)->int_clr = (1 << 11); -+} -+ -+/* -+ * ip7500media_tsc2007_get_pendown_state -+ */ -+static int ip7500media_tsc2007_get_pendown_state(void) -+{ -+ return !gpio_get_value(GPIO_RD_1); -+} -+ -+static struct tsc2007_platform_data ip7500media_tsc2007_data = { -+ .model = 2007, -+ .x_plate_ohms = 350, -+ .get_pendown_state = ip7500media_tsc2007_get_pendown_state, -+ .init_platform_hw = ip7500media_tsc2007_init_platform_hw, -+ .exit_platform_hw = ip7500media_tsc2007_exit_platform_hw, -+ .clear_penirq = ip7500media_tsc2007_clear_penirq, -+}; -+ -+/****************************************************************************** -+ * 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, -+ }, -+}; -+ -+/* -+ * Additional I2C devices to add when a LCD adapter board is present -+ */ -+static struct i2c_board_info __initdata ip7500media_lcd_adapter_i2c_board_info[] = { -+ { -+ I2C_BOARD_INFO("tsc2007", 0x48), -+ .irq = PORT_OTHER_INT(RD), -+ .platform_data = &ip7500media_tsc2007_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, -+ }, -+}; -+ -+/* -+ * Virtual Frame Buffer device for use with LCD Adapter -+ */ -+static struct platform_device ip7500media_vfb_device = { -+ .name = "ubicom32vfb", -+ .id = -1, -+}; -+ -+/* -+ * vdc_override: -+ * 0: no override (auto-detect) -+ * 1: force vdc usage -+ * 2: force lcd adapter usage -+ */ -+static int __initdata vdc_override = 0; -+ -+/* -+ * ip7500media_set_forcevdc -+ * Called when forcevdc is present on the kernel boot line -+ */ -+static int __init ip7500media_set_forcevdc(char *str) -+{ -+ if (str[0] == '1') { -+ vdc_override = 1; -+ } else { -+ vdc_override = 2; -+ } -+ return 1; -+} -+ -+/* -+ * ip7500media_video_init -+ * Called late to determine what kind of video we have on this board -+ */ -+static int __init ip7500media_video_init(void) -+{ -+ struct i2c_adapter *adap; -+ struct i2c_msg msg[1]; -+ unsigned char *data; -+ unsigned char checksum; -+ int err; -+ int i; -+ -+ if (vdc_override == 1) { -+ printk(KERN_INFO "Force VDCTIO mode\n"); -+ goto no_adapter; -+ } -+ if (vdc_override == 2) { -+ printk(KERN_INFO "Force LCD Adapter Board mode\n"); -+ return 0; -+ } -+ -+ /* -+ * Check to see if there is an EEPROM out there. If we see an -+ * EEPROM then we will assume a LCD Adapter Board (8007-092x) -+ * exists. -+ */ -+ data = kmalloc(256, GFP_KERNEL); -+ if (!data) { -+ printk(KERN_WARNING "%s: Failed to allocate memory\n", __FUNCTION__); -+ return -ENOMEM; -+ } -+ -+ adap = i2c_get_adapter(0); -+ if (!adap) { -+ printk(KERN_WARNING "%s: Failed to get i2c adapter\n", __FUNCTION__); -+ kfree(data); -+ return -ENODEV; -+ } -+ data[0] = 0; -+ msg->addr = 0x50; -+ msg->flags = 0; -+ msg->len = 1; -+ msg->buf = data; -+ err = i2c_transfer(adap, msg, 1); -+ if (err < 0) { -+ goto no_adapter; -+ } -+ -+ msg->addr = 0x50; -+ msg->flags = I2C_M_RD; -+ msg->len = 256; -+ msg->buf = data; -+ err = i2c_transfer(adap, msg, 1); -+ if (err < 0) { -+ goto no_adapter; -+ } -+ -+ i2c_put_adapter(adap); -+ -+ /* -+ * Verify the checksum -+ */ -+ checksum = 0xff; -+ for (i = 0; i < 255; i++) { -+ checksum ^= data[i]; -+ } -+ if (checksum != data[255]) { -+ printk(KERN_WARNING "%s: Checksum mismatch\n", __FUNCTION__); -+ } -+ -+ kfree(data); -+ -+ /* -+ * Bring up VFB -+ */ -+ platform_device_register(&ip7500media_vfb_device); -+ -+ /* -+ * Add the i2c devices on the LCD Adapter board. (We have to use i2c_new_device -+ * since it's late in the boot process.) -+ */ -+ printk(KERN_INFO "%s: registering LCD Adapter board i2c resources\n", __FUNCTION__); -+ for (i = 0; i < ARRAY_SIZE(ip7500media_lcd_adapter_i2c_board_info); i++) { -+ i2c_new_device(adap, &ip7500media_lcd_adapter_i2c_board_info[i]); -+ } -+ -+ i2c_put_adapter(adap); -+ -+ return 0; -+ -+ /* -+ * No LCD Adapter board, bring up VDC -+ */ -+no_adapter: -+ vdc_tio_init(); -+ return 0; -+} -+late_initcall(ip7500media_video_init); -+__setup("forcevdc=", ip7500media_set_forcevdc); -+ -+/* -+ * ip7500media_init -+ * Called to add the devices which we have on this board -+ */ -+static int __init ip7500media_init(void) -+{ -+ struct platform_device *audio_dev; -+ int have_ethernet = (devtree_find_node("eth_lan") != 0); -+ -+ board_init(); -+ -+ ubi_gpio_init(); -+ -+#ifdef CONFIG_UIO_UBICOM32RING -+ ring_tio_init("decoder_ring"); -+#endif -+ -+ /* -+ * Register all of the devices we have on this board -+ */ -+ printk(KERN_INFO "%s: registering device resources\n", __FUNCTION__); -+ platform_device_register(&ip7500media_i2c_device); -+ platform_device_register(&ip7500media_backlight_device); -+ -+ /* -+ * If ethernet doesn't exist then we can init the lcdpower -+ */ -+ if (!have_ethernet) { -+ platform_device_register(&ip7500media_lcdpower_device); -+ } -+ -+ /* -+ * Allocate the audio drivers. SPDIF not supported on boards with ethernet. -+ */ -+ audio_dev = audio_device_alloc("snd-ubi32-cs4350", "audio", "audio-i2sout", 0); -+ if (audio_dev) { -+ ip7500media_i2c_board_info[0].platform_data = audio_dev; -+ } -+ -+ if (!have_ethernet) { -+ struct platform_device *audio_dev2; -+ -+ audio_dev2 = audio_device_alloc("snd-ubi32-generic", "audio", "audio-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, 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 -+ */ -+ -+ printk("IP7500 Media Board\n"); -+ -+ return 0; -+} -+ -+arch_initcall(ip7500media_init); ---- /dev/null -+++ b/arch/ubicom32/mach-ip7k/board-ip7500module.c -@@ -0,0 +1,55 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+ -+/* -+ * 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/board-ip7500wspkr.c -@@ -0,0 +1,101 @@ -+/* -+ * arch/ubicom32/mach-ip7k/board-ip7500wspkr.c -+ * Support for IP7500 Wireless Speaker board. -+ * -+ * This file supports the IP7500 Wireless Speaker board: -+ * 8007-1210 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 . -+ */ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+static struct i2c_board_info __initdata ip7500wspkr_i2c_board_info[] = { -+ /* -+ * U6, CS4350 DAC, address 0x4B -+ */ -+ { -+ .type = "cs4350", -+ .addr = 0x4B, -+ }, -+}; -+ -+/* -+ * I2C bus on the board, SDA PE4, SCL PE5 -+ */ -+static struct i2c_gpio_platform_data ip7500wspkr_i2c_data = { -+ .sda_pin = GPIO_RD_5, -+ .scl_pin = GPIO_RD_6, -+ .sda_is_open_drain = 0, -+ .scl_is_open_drain = 0, -+ .udelay = 50, -+}; -+ -+static struct platform_device ip7500wspkr_i2c_device = { -+ .name = "i2c-gpio", -+ .id = 0, -+ .dev = { -+ .platform_data = &ip7500wspkr_i2c_data, -+ }, -+}; -+ -+static struct platform_device *ip7500wspkr_devices[] __initdata = { -+ &ip7500wspkr_i2c_device, -+}; -+ -+/* -+ * ip7500wspkr_init -+ * Called to add the devices which we have on this board -+ */ -+static int __init ip7500wspkr_init(void) -+{ -+ struct platform_device *audio_dev; -+ struct platform_device *audio_dev2; -+ -+ board_init(); -+ -+ ubi_gpio_init(); -+ -+ platform_add_devices(ip7500wspkr_devices, ARRAY_SIZE(ip7500wspkr_devices)); -+ -+ audio_dev = audio_device_alloc("snd-ubi32-cs4350", "audio", "audio-i2sout", 0); -+ if (audio_dev) { -+ ip7500wspkr_i2c_board_info[0].platform_data = audio_dev; -+ } -+ -+ audio_dev2 = audio_device_alloc("snd-ubi32-generic", "audio", "audio-spdifout", 0); -+ if (audio_dev2) { -+ platform_device_register(audio_dev2); -+ } -+ -+ printk(KERN_INFO "%s: registering i2c resources\n", __FUNCTION__); -+ i2c_register_board_info(0, ip7500wspkr_i2c_board_info, ARRAY_SIZE(ip7500wspkr_i2c_board_info)); -+ -+ printk(KERN_INFO "IP7500 Wireless Speaker Board\n"); -+ -+ return 0; -+} -+ -+arch_initcall(ip7500wspkr_init); ---- /dev/null -+++ b/arch/ubicom32/mach-ip7k/Kconfig -@@ -0,0 +1,205 @@ -+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 LCD_CLASS_DEVICE -+ select LCD_UBICOM32POWER -+ select BACKLIGHT_LCD_SUPPORT -+ select BACKLIGHT_CLASS_DEVICE -+ select BACKLIGHT_UBICOM32 -+ select SND_UBI32 -+ select MMC_UBICOM32 -+ select MMC -+ select MMC_BLOCK -+ 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 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 IP7160RGWLCD -+ bool "IP7160RGWLCD" -+ select UBICOM32_V4 -+ select UBICOM_INPUT -+ select NEW_LEDS -+ select LEDS_CLASS -+ select LEDS_GPIO -+ select SPI -+ select SPI_UBICOM32_GPIO -+ select VLAN_8021Q -+ select UBICOM_SWITCH -+ select UBICOM_SWITCH_BCM539X -+ select INPUT_TOUCHSCREEN -+ select TOUCHSCREEN_TSC2007 -+ select FB -+ select FB_UBICOM32_VIRTUAL -+ select I2C -+ select I2C_GPIO -+ help -+ Ubicom IP7160 RGW Eval, supports: -+ 8007-0112 v1.2 + 8007-1410 v1.0 -+ -+ With Ubicom LCD Adapter -+ 8007-0920 v2.0 -+ 8007-0921 v2.1 -+ -+ -+config IP7160BRINGUP -+ bool "IP7160BRINGUP" -+ select UBICOM32_V4 -+ select NEW_LEDS -+ select LEDS_CLASS -+ select LEDS_GPIO -+ 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 -+ help -+ IP7160 Digital Picture Frame board, supports: -+ 8007-0211 Rev 1.1 -+ -+config IP7500MODULE -+ bool "IP7500MODULE" -+ select UBICOM32_V4 -+ 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 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 FB_UBICOM32_VIRTUAL -+ select FB_UBICOM32_VIRTUAL_NOAUTO -+ select LCD_CLASS_DEVICE -+ select LCD_UBICOM32POWER -+ select BACKLIGHT_LCD_SUPPORT -+ select BACKLIGHT_CLASS_DEVICE -+ select BACKLIGHT_UBICOM32 -+ select INPUT_TOUCHSCREEN -+ select TOUCHSCREEN_TSC2007 -+ select SOUND -+ select SND -+ select SND_UBI32 -+ select SND_UBI32_AUDIO_CS4350 -+ select MMC_UBICOM32 -+ select MMC -+ select MMC_BLOCK -+ help -+ IP7500 Media Board w/ IP7500 CPU Module board, supports: -+ 8007-0610 v1.0 w/ 8007-0510 v1.0 -+ 8007-0610 v1.0 w/ 8007-0510 v1.0 NOPHY -+ 8007-0610 v1.0 w/ 8007-0511 v1.1 NOPHY -+ -+ Also supports optional LCD Adapter board: -+ 8006-0920 v2.0 -+ 8006-0921 v2.1 -+ -+ Please see ip7500media.c for more details. -+ -+config IP7500WSPKR -+ bool "IP7500WSPKR" -+ select UBICOM32_V4 -+ select I2C -+ select I2C_GPIO -+ select SOUND -+ select SND -+ select SND_UBI32 -+ select SND_UBI32_AUDIO_CS4350 -+ help -+ IP7500 Wireless Speaker Board, supports: -+ 8007-1210 v1.0 -+ -+ Please see ip7500wspkr.c for more details. -+ -+config IP7500IAP -+ bool "IP7500IAP" -+ select UBICOM32_V4 -+ select I2C -+ select I2C_GPIO -+ select FB -+ select FB_UBICOM32_VIRTUAL -+ select SOUND -+ select SND -+ select SND_UBI32 -+ select SND_UBI32_AUDIO_CS4350 -+ select RTC_CLASS -+ select RTC_DRV_S35390A -+ select INPUT_TOUCHSCREEN -+ select TOUCHSCREEN_TSC2007 -+ select BACKLIGHT_LCD_SUPPORT -+ select BACKLIGHT_CLASS_DEVICE -+ select BACKLIGHT_UBICOM32 -+ help -+ IP7500 Internet Audio Player, supports: -+ 8007-1110 v1.0 -+ -+ Please see ip7500iap.c for more details. -+ -+ -+ Please see ip7500media.c for more details. ---- /dev/null -+++ b/arch/ubicom32/mach-ip7k/Makefile -@@ -0,0 +1,38 @@ -+# -+# 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 . -+# -+# 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_IP7160RGWLCD) += 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 -+obj-$(CONFIG_IP7500WSPKR) += board-ip7500wspkr.o -+obj-$(CONFIG_IP7500IAP) += board-ip7500iap.o ---- /dev/null -+++ b/arch/ubicom32/Makefile -@@ -0,0 +1,104 @@ -+# -+# arch/ubicom32/Makefile -+# -+# -+# (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 . -+# -+# 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 -DIP5000 -+cflags-$(CONFIG_UBICOM32_V4) := -march=ubicom32v4 -DIP7000 -+ -+ldflags-$(CONFIG_LINKER_RELAXATION) := --relax -+LDFLAGS_vmlinux := $(ldflags-y) -+ -+GCCLIBDIR := $(dir $(shell $(CC) $(cflags-y) -print-libgcc-file-name)) -+GCC_LIBS := $(GCCLIBDIR)/libgcc.a -+ -+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/ -+libs-y += $(GCC_LIBS) -+ -+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,80 @@ -+/* -+ * arch/ubicom32/mm/fault.c -+ * Ubicom32 architecture page fault implementation. -+ * -+ * (C) Copyright 2009, Ubicom, Inc. -+ * Copyright (C) 1998 D. Jeff Dionne , -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+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,262 @@ -+/* -+ * arch/ubicom32/mm/init.c -+ * Ubicom32 architecture virtual memory initialization. -+ * -+ * (C) Copyright 2009, Ubicom, Inc. -+ * Copyright (C) 1998 D. Jeff Dionne , -+ * Kenneth Albanowski , -+ * 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 -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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, ram_start, ram_end, len; -+ extern char _etext, _stext, _sdata, _ebss, __init_begin, __init_end; -+ -+ 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 */ -+ processor_dram(&ram_start, &ram_end); -+ len = (ram_end - ram_start) + OCMSIZE; -+#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, -+ * Copyright (C) 2000-2002 David McCullough -+ * -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 -+# -+# -+# (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 . -+# -+# 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,58 @@ -+/* -+ * arch/ubicom32/mm/memory.c -+ * Ubicom32 architecture kernel_map() implementation. -+ * -+ * (C) Copyright 2009, Ubicom, Inc. -+ * Copyright (C) 1998 Kenneth Albanowski , -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+/* -+ * 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,487 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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; -+int ubi32_ocm_skbuf_max = 21, ubi32_ocm_skbuf, ubi32_ddr_skbuf; -+ -+/* -+ * OCM area for storing code -+ */ -+extern asmlinkage void *__ocm_free_begin; -+extern asmlinkage void *__ocm_free_end; -+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; -+ -+ len = sprintf(&buf[len], "--- OCM SKB usage (max RX buf %d)\n" -+ "(SKB in OCM) %d - (SKB in DDR) %d\n", -+ ubi32_ocm_skbuf_max, -+ ubi32_ocm_skbuf, -+ ubi32_ddr_skbuf); -+ -+ len += sprintf(&buf[len], "--- OCM Data Heap Size\n" -+ "%p-%p %10i\n", -+ ((void *)&__ocm_free_begin), -+ ((void *)&__ocm_free_end), -+ ((unsigned int)&__ocm_free_end) - -+ ((unsigned int)&__ocm_free_begin)); -+ -+ if (_ocm_proc_read(buf, &len, count - len, "Inst Heap", -+ &ocm_inst_heap)) -+ goto not_done; -+ *eof = 1; -+ not_done: -+ return len; -+} -+ -+static int ocm_proc_write(struct file *file, const char __user *buffer, -+ unsigned long count, void *data) -+{ -+ int n, v; -+ char in[8]; -+ -+ if (count > sizeof(in)) -+ return -EINVAL; -+ -+ if (copy_from_user(in, buffer, count)) -+ return -EFAULT; -+ in[count-1] = 0; -+ -+ printk(KERN_INFO "OCM skb alloc max = %s\n", in); -+ -+ n = 0; -+ v = 0; -+ while ((in[n] >= '0') && (in[n] <= '9')) { -+ v = v * 10 + (int)(in[n] - '0'); -+ n++; -+ } -+ -+ if (v == 0) -+ return -EINVAL; -+ -+ ubi32_ocm_skbuf_max = v; -+ ubi32_ocm_skbuf = ubi32_ddr_skbuf = 0; -+ -+ return count; -+} -+ -+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->read_proc = ocm_proc_read; -+ ptr->write_proc = ocm_proc_write; -+ 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 . -+# -+# 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 -+ * . -+ * -+ * 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 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+/* For identifying userland vs kernel address */ -+#include -+#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 -@@ -148,3 +148,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 -@@ -15,3 +15,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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 -@@ -266,3 +266,10 @@ config GPIOMMC_CONFIGFS - help - This option automatically enables configfs support for gpiommc - if configfs is available. -+ -+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 ---- a/drivers/mmc/host/Makefile -+++ b/drivers/mmc/host/Makefile -@@ -30,4 +30,5 @@ obj-$(CONFIG_MMC_S3C) += s3cmci.o - obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o - obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o - obj-$(CONFIG_GPIOMMC) += gpiommc.o -+obj-$(CONFIG_MMC_UBICOM32) += ubicom32sd.o - ---- /dev/null -+++ b/drivers/mmc/host/ubicom32sd.c -@@ -0,0 +1,773 @@ -+/* -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#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,31 @@ config M25PXX_USE_FAST_READ - help - This option enables FAST_READ access supported by ST M25Pxx. - -+config MTD_UBI32_NAND_SPI_ER -+ tristate "UBI32_NAND SPI-ER support" -+ help -+ This driver supports the Micron MT29F1G01 SPI-ER NAND flash chip -+ using the built in flash controller on the Ubicom32 architecture. -+ Partial page writes are not supported by this driver. -+ -+config MTD_NAND_SPI_ER -+ tristate "NAND SPI-ER support" -+ help -+ This driver supports the Micron MT29F1G01 SPI-ER NAND flash chip -+ using a generic SPI bus. Partial page writes are supported -+ by this driver. -+ -+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,6 @@ 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 -+obj-$(CONFIG_MTD_NAND_SPI_ER) += nand-spi-er.o -+obj-$(CONFIG_MTD_UBI32_NAND_SPI_ER) += ubi32-nand-spi-er.o ---- /dev/null -+++ b/drivers/mtd/devices/nand-spi-er.c -@@ -0,0 +1,1017 @@ -+/* -+ * Micron SPI-ER NAND Flash Memory -+ * -+ * (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 . -+*/ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+#include -+ -+#define NAND_SPI_ER_BLOCK_FROM_ROW(row) (row >> 6) -+ -+#define NAND_SPI_ER_STATUS_P_FAIL (1 << 3) -+#define NAND_SPI_ER_STATUS_E_FAIL (1 << 2) -+#define NAND_SPI_ER_STATUS_OIP (1 << 0) -+ -+#define NAND_SPI_ER_LAST_ROW_INVALID 0xFFFFFFFF -+#define NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET 0x08 -+ -+struct nand_spi_er_device { -+ const char *name; -+ -+ uint8_t id0; -+ uint8_t id1; -+ -+ unsigned int blocks; -+ unsigned int pages_per_block; -+ unsigned int page_size; -+ unsigned int write_size; -+ unsigned int erase_size; -+}; -+ -+struct nand_spi_er { -+ char name[24]; -+ -+ const struct nand_spi_er_device *device; -+ -+ struct mutex lock; -+ struct spi_device *spi; -+ -+ struct mtd_info mtd; -+ -+ unsigned int last_row; /* the last row we fetched */ -+ -+ /* -+ * Bad block table (MUST be last in strcuture) -+ */ -+ unsigned long nbb; -+ unsigned long bbt[0]; -+}; -+ -+const struct nand_spi_er_device nand_spi_er_devices[] = { -+ { -+ name: "MT29F1G01ZDC", -+ id0: 0x2C, -+ id1: 0x12, -+ blocks: 1024, -+ pages_per_block: 64, -+ page_size: 2048, -+ write_size: 512, -+ erase_size: 64 * 2048, -+ }, -+ { -+ name: "MT29F1G01ZDC", -+ id0: 0x2C, -+ id1: 0x13, -+ blocks: 1024, -+ pages_per_block: 64, -+ page_size: 2048, -+ write_size: 512, -+ erase_size: 64 * 2048, -+ }, -+}; -+ -+static int read_only = 0; -+module_param(read_only, int, 0); -+MODULE_PARM_DESC(read_only, "Leave device locked"); -+ -+/* -+ * nand_spi_er_get_feature -+ * Get Feature register -+ */ -+static int nand_spi_er_get_feature(struct nand_spi_er *chip, int reg, uint8_t *data) -+{ -+ uint8_t txbuf[2]; -+ uint8_t rxbuf[1]; -+ int res; -+ -+ txbuf[0] = 0x0F; -+ txbuf[1] = reg; -+ res = spi_write_then_read(chip->spi, txbuf, 2, rxbuf, 1); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed get feature res=%d\n", chip->name, res); -+ return res; -+ } -+ *data = rxbuf[0]; -+ return 0; -+} -+ -+/* -+ * nand_spi_er_busywait -+ * Wait until the chip is not busy -+ */ -+static int nand_spi_er_busywait(struct nand_spi_er *chip, uint8_t *data) -+{ -+ int i; -+ -+ for (i = 0; i < 100; i++) { -+ int res = nand_spi_er_get_feature(chip, 0xC0, data); -+ if (res) { -+ return res; -+ } -+ if (!(*data & NAND_SPI_ER_STATUS_OIP)) { -+ break; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * nand_spi_er_erase -+ * Erase a block, parameters must be block aligned -+ */ -+static int nand_spi_er_erase(struct mtd_info *mtd, struct erase_info *instr) -+{ -+ struct nand_spi_er *chip = mtd->priv; -+ struct spi_device *spi = chip->spi; -+ uint8_t txbuf[4]; -+ int res; -+ -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: erase addr:%x len:%x\n", chip->name, instr->addr, instr->len); -+ -+ if ((instr->addr + instr->len) > mtd->size) { -+ return -EINVAL; -+ } -+ -+ if (instr->addr & (chip->device->erase_size - 1)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: erase address is not aligned %x\n", chip->name, instr->addr); -+ return -EINVAL; -+ } -+ -+ if (instr->len & (chip->device->erase_size - 1)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: erase len is not aligned %x\n", chip->name, instr->len); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&chip->lock); -+ chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; -+ -+ while (instr->len) { -+ uint32_t block = instr->addr >> 17; -+ uint32_t row = block << 6; -+ uint8_t stat; -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: block erase row:%x block:%x addr:%x rem:%x\n", chip->name, row, block, instr->addr, instr->len); -+ -+ /* -+ * Write enable -+ */ -+ txbuf[0] = 0x06; -+ res = spi_write(spi, txbuf, 1); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); -+ mutex_unlock(&chip->lock); -+ return res; -+ } -+ -+ /* -+ * Test for bad block -+ */ -+ if (test_bit(block, chip->bbt)) { -+ instr->fail_addr = block << 17; -+ instr->state = MTD_ERASE_FAILED; -+ res = -EBADMSG; -+ goto done; -+ } -+ -+ /* -+ * Block erase -+ */ -+ txbuf[0] = 0xD8; -+ txbuf[1] = 0x00; -+ txbuf[2] = row >> 8; -+ txbuf[3] = row & 0xFF; -+ res = spi_write(spi, txbuf, 4); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed block erase res=%d\n", chip->name, res); -+ instr->fail_addr = block << 17; -+ instr->state = MTD_ERASE_FAILED; -+ goto done; -+ } -+ -+ /* -+ * Wait -+ */ -+ res = nand_spi_er_busywait(chip, &stat); -+ if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { -+ instr->fail_addr = block << 17; -+ instr->state = MTD_ERASE_FAILED; -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); -+ if (res) { -+ goto done; -+ } -+ -+ /* -+ * Chip is stuck? -+ */ -+ res = -EIO; -+ goto done; -+ } -+ -+ /* -+ * Check the status register -+ */ -+ if (stat & NAND_SPI_ER_STATUS_E_FAIL) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: E_FAIL signalled (%02x)\n", chip->name, stat); -+ instr->fail_addr = block << 17; -+ instr->state = MTD_ERASE_FAILED; -+ goto done; -+ } -+ -+ /* -+ * Next -+ */ -+ block++; -+ instr->len -= chip->device->erase_size; -+ instr->addr += chip->device->erase_size; -+ } -+ -+ instr->state = MTD_ERASE_DONE; -+ -+ mutex_unlock(&chip->lock); -+ return 0; -+ -+done: -+ /* -+ * Write disable -+ */ -+ txbuf[0] = 0x04; -+ res = spi_write(spi, txbuf, 1); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); -+ } -+ -+ mutex_unlock(&chip->lock); -+ -+ mtd_erase_callback(instr); -+ return 0; -+} -+ -+/* -+ * nand_spi_er_read -+ * -+ * return -EUCLEAN: ecc error recovered -+ * return -EBADMSG: ecc error not recovered -+*/ -+static int nand_spi_er_read(struct mtd_info *mtd, loff_t from, size_t len, -+ size_t *retlen, u_char *buf) -+{ -+ struct nand_spi_er *chip = mtd->priv; -+ struct spi_device *spi = chip->spi; -+ -+ uint32_t row; -+ uint32_t column; -+ int retval = 0; -+ -+ *retlen = 0; -+ DEBUG(MTD_DEBUG_LEVEL2, "%s: read block from %llx len %d into %p\n", chip->name, from, len, buf); -+ -+ /* -+ * Zero length reads, nothing to do -+ */ -+ if (len == 0) { -+ return 0; -+ } -+ -+ /* -+ * Reject reads which go over the end of the flash -+ */ -+ if ((from + len) > mtd->size) { -+ return -EINVAL; -+ } -+ -+ /* -+ * Get the row and column address to start at -+ */ -+ row = from >> 11; -+ column = from & 0x7FF; -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: row=%x %d column=%x %d last_row=%x %d\n", chip->name, row, row, column, column, chip->last_row, chip->last_row); -+ -+ /* -+ * Read the data from the chip -+ */ -+ mutex_lock(&chip->lock); -+ while (len) { -+ uint8_t stat; -+ uint8_t txbuf[4]; -+ struct spi_message message; -+ struct spi_transfer x[2]; -+ int res; -+ size_t toread; -+ -+ /* -+ * Figure out how much to read -+ * -+ * If we are reading from the middle of a page then the most we -+ * can read is to the end of the page -+ */ -+ toread = len; -+ if (toread > (chip->device->page_size - column)) { -+ toread = chip->device->page_size - column; -+ } -+ -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: buf=%p toread=%x row=%x column=%x last_row=%x\n", chip->name, buf, toread, row, column, chip->last_row); -+ -+ if (chip->last_row != row) { -+ /* -+ * Check if the block is bad -+ */ -+ if (test_bit(NAND_SPI_ER_BLOCK_FROM_ROW(row), chip->bbt)) { -+ mutex_unlock(&chip->lock); -+ return -EBADMSG; -+ } -+ -+ /* -+ * Load the appropriate page -+ */ -+ txbuf[0] = 0x13; -+ txbuf[1] = 0x00; -+ txbuf[2] = row >> 8; -+ txbuf[3] = row & 0xFF; -+ res = spi_write(spi, txbuf, 4); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed page load res=%d\n", chip->name, res); -+ mutex_unlock(&chip->lock); -+ return res; -+ } -+ -+ /* -+ * Wait -+ */ -+ res = nand_spi_er_busywait(chip, &stat); -+ if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); -+ if (res) { -+ mutex_unlock(&chip->lock); -+ return res; -+ } -+ -+ /* -+ * Chip is stuck? -+ */ -+ mutex_unlock(&chip->lock); -+ return -EIO; -+ } -+ -+ /* -+ * Check the ECC bits -+ */ -+ stat >>= 4; -+ if (stat == 1) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: ECC recovered, row=%x\n", chip->name, row); -+ retval = -EUCLEAN; -+ } -+ if (stat == 2) { -+ DEBUG(MTD_DEBUG_LEVEL0, "%s: failed ECC, row=%x\n", chip->name, row); -+ chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; -+ mutex_unlock(&chip->lock); -+ return -EBADMSG; -+ } -+ -+ } -+ -+ chip->last_row = row; -+ -+ /* -+ * Read out the data -+ */ -+ spi_message_init(&message); -+ memset(x, 0, sizeof(x)); -+ -+ txbuf[0] = 0x03; -+ txbuf[1] = column >> 8; -+ txbuf[2] = column & 0xFF; -+ txbuf[3] = 0; -+ x[0].tx_buf = txbuf; -+ x[0].len = 4; -+ spi_message_add_tail(&x[0], &message); -+ -+ x[1].rx_buf = buf; -+ x[1].len = toread; -+ spi_message_add_tail(&x[1], &message); -+ -+ res = spi_sync(spi, &message); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed data read res=%d\n", chip->name, res); -+ mutex_unlock(&chip->lock); -+ return res; -+ } -+ buf += toread; -+ len -= toread; -+ *retlen += toread; -+ -+ /* -+ * For the next page, increment the row and always start at column 0 -+ */ -+ column = 0; -+ row++; -+ } -+ -+ mutex_unlock(&chip->lock); -+ return retval; -+} -+ -+/* -+ * nand_spi_er_write -+ */ -+#define NOT_ALIGNED(x) ((x & (device->write_size - 1)) != 0) -+static int nand_spi_er_write(struct mtd_info *mtd, loff_t to, size_t len, -+ size_t *retlen, const u_char *buf) -+{ -+ struct nand_spi_er *chip = mtd->priv; -+ struct spi_device *spi = chip->spi; -+ const struct nand_spi_er_device *device = chip->device; -+ uint32_t row; -+ uint32_t col; -+ uint8_t txbuf[4]; -+ int res; -+ size_t towrite; -+ -+ DEBUG(MTD_DEBUG_LEVEL2, "%s: write block to %llx len %d from %p\n", chip->name, to, len, buf); -+ -+ *retlen = 0; -+ -+ /* -+ * nothing to write -+ */ -+ if (!len) { -+ return 0; -+ } -+ -+ /* -+ * Reject writes which go over the end of the flash -+ */ -+ if ((to + len) > mtd->size) { -+ return -EINVAL; -+ } -+ -+ /* -+ * Check to see if everything is page aligned -+ */ -+ if (NOT_ALIGNED(to) || NOT_ALIGNED(len)) { -+ printk(KERN_NOTICE "nand_spi_er_write: Attempt to write non page aligned data\n"); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&chip->lock); -+ chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; -+ -+ /* -+ * If the first write is a partial write then write at most the number of -+ * bytes to get us page aligned and then the remainder will be -+ * page aligned. The last bit may be a partial page as well. -+ */ -+ col = to & (device->page_size - 1); -+ towrite = device->page_size - col; -+ if (towrite > len) { -+ towrite = len; -+ } -+ -+ /* -+ * Write the data -+ */ -+ row = to >> 11; -+ while (len) { -+ struct spi_message message; -+ struct spi_transfer x[2]; -+ uint8_t stat; -+ -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: write %p to row:%x col:%x len:%x rem:%x\n", chip->name, buf, row, col, towrite, len); -+ -+ /* -+ * Write enable -+ */ -+ txbuf[0] = 0x06; -+ res = spi_write(spi, txbuf, 1); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); -+ mutex_unlock(&chip->lock); -+ return res; -+ } -+ -+ /* -+ * Write the data into the cache -+ */ -+ spi_message_init(&message); -+ memset(x, 0, sizeof(x)); -+ txbuf[0] = 0x02; -+ txbuf[1] = col >> 8; -+ txbuf[2] = col & 0xFF; -+ x[0].tx_buf = txbuf; -+ x[0].len = 3; -+ spi_message_add_tail(&x[0], &message); -+ x[1].tx_buf = buf; -+ x[1].len = towrite; -+ spi_message_add_tail(&x[1], &message); -+ res = spi_sync(spi, &message); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed cache write res=%d\n", chip->name, res); -+ goto done; -+ } -+ -+ /* -+ * Program execute -+ */ -+ txbuf[0] = 0x10; -+ txbuf[1] = 0x00; -+ txbuf[2] = row >> 8; -+ txbuf[3] = row & 0xFF; -+ res = spi_write(spi, txbuf, 4); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed prog execute res=%d\n", chip->name, res); -+ goto done; -+ } -+ -+ /* -+ * Wait -+ */ -+ res = nand_spi_er_busywait(chip, &stat); -+ if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); -+ if (res) { -+ goto done; -+ } -+ -+ /* -+ * Chip is stuck? -+ */ -+ res = -EIO; -+ goto done; -+ } -+ -+ if (stat & (1 << 3)) { -+ res = -EBADMSG; -+ goto done; -+ } -+ -+ row++; -+ buf += towrite; -+ len -= towrite; -+ *retlen += towrite; -+ -+ /* -+ * At this point, we are always page aligned so start at column 0. -+ * Note we may not have a full page to write at the end, hence the -+ * check if towrite > len. -+ */ -+ col = 0; -+ towrite = device->page_size; -+ if (towrite > len) { -+ towrite = len; -+ } -+ } -+ -+ mutex_unlock(&chip->lock); -+ return res; -+ -+done: -+ /* -+ * Write disable -+ */ -+ txbuf[0] = 0x04; -+ res = spi_write(spi, txbuf, 1); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); -+ } -+ -+ mutex_unlock(&chip->lock); -+ -+ return res; -+} -+ -+/* -+ * nand_spi_er_isbad -+ */ -+static int nand_spi_er_isbad(struct mtd_info *mtd, loff_t ofs) -+{ -+ struct nand_spi_er *chip = mtd->priv; -+ uint32_t block; -+ -+ if (ofs & (chip->device->erase_size - 1)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); -+ return -EINVAL; -+ } -+ -+ block = ofs >> 17; -+ -+ return test_bit(block, chip->bbt); -+} -+ -+/* -+ * nand_spi_er_markbad -+ */ -+static int nand_spi_er_markbad(struct mtd_info *mtd, loff_t ofs) -+{ -+ struct nand_spi_er *chip = mtd->priv; -+ struct spi_device *spi = chip->spi; -+ uint32_t block; -+ uint32_t row; -+ uint8_t txbuf[7]; -+ int res; -+ uint8_t stat; -+ -+ if (ofs & (chip->device->erase_size - 1)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); -+ return -EINVAL; -+ } -+ -+ block = ofs >> 17; -+ -+ /* -+ * If it's already marked bad, no need to mark it -+ */ -+ if (test_bit(block, chip->bbt)) { -+ return 0; -+ } -+ -+ /* -+ * Mark it in our cache -+ */ -+ __set_bit(block, chip->bbt); -+ -+ /* -+ * Write the user bad block mark. If it fails, then we really -+ * can't do anything about it. -+ */ -+ mutex_lock(&chip->lock); -+ chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; -+ -+ /* -+ * Write enable -+ */ -+ txbuf[0] = 0x06; -+ res = spi_write(spi, txbuf, 1); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); -+ mutex_unlock(&chip->lock); -+ return res; -+ } -+ -+ /* -+ * Write the mark -+ */ -+ txbuf[0] = 0x84; -+ txbuf[1] = 0x08; -+ txbuf[2] = NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET; -+ txbuf[3] = 0xde; -+ txbuf[4] = 0xad; -+ txbuf[5] = 0xbe; -+ txbuf[6] = 0xef; -+ res = spi_write(spi, txbuf, 7); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write mark res=%d\n", chip->name, res); -+ goto done; -+ } -+ -+ /* -+ * Program execute -+ */ -+ row = ofs >> 11; -+ txbuf[0] = 0x10; -+ txbuf[1] = 0x00; -+ txbuf[2] = row >> 8; -+ txbuf[3] = row & 0xFF; -+ res = spi_write(spi, txbuf, 4); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed program execute res=%d\n", chip->name, res); -+ goto done; -+ } -+ -+ /* -+ * Wait -+ */ -+ res = nand_spi_er_busywait(chip, &stat); -+ if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); -+ if (res) { -+ goto done; -+ } -+ -+ /* -+ * Chip is stuck? -+ */ -+ res = -EIO; -+ goto done; -+ } -+ -+ if (stat & (1 << 3)) { -+ res = -EBADMSG; -+ } -+ -+done: -+ /* -+ * Write disable -+ */ -+ txbuf[0] = 0x04; -+ res = spi_write(spi, txbuf, 1); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); -+ } -+ -+ mutex_unlock(&chip->lock); -+ -+ return res; -+} -+ -+/* -+ * nand_spi_er_read_bbt -+ */ -+static int nand_spi_er_read_bbt(struct nand_spi_er *chip) -+{ -+ int j; -+ for (j = 0; j < chip->device->blocks; j++) { -+ uint8_t txbuf[4]; -+ uint8_t rxbuf[16]; -+ uint32_t bbmark; -+ int res; -+ unsigned short row = j << 6; -+ uint8_t stat; -+ -+ /* -+ * Read Page -+ */ -+ txbuf[0] = 0x13; -+ txbuf[1] = 0x00; -+ txbuf[2] = row >> 8; -+ txbuf[3] = row & 0xFF; -+ res = spi_write(chip->spi, txbuf, 4); -+ if (res) { -+ return res; -+ } -+ -+ /* -+ * Wait -+ */ -+ res = nand_spi_er_busywait(chip, &stat); -+ if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); -+ if (res) { -+ return res; -+ } -+ -+ /* -+ * Chip is stuck? -+ */ -+ return -EIO; -+ } -+ -+ /* -+ * Check factory bad block mark -+ */ -+ txbuf[0] = 0x03; -+ txbuf[1] = 0x08; -+ txbuf[2] = 0x00; -+ txbuf[3] = 0x00; -+ res = spi_write_then_read(chip->spi, txbuf, 4, rxbuf, 16); -+ if (res) { -+ return res; -+ } -+ if (rxbuf[0] != 0xFF) { -+ chip->nbb++; -+ __set_bit(j, chip->bbt); -+ continue; -+ } -+ -+ memcpy(&bbmark, &rxbuf[8], 4); -+ if (bbmark == 0xdeadbeef) { -+ chip->nbb++; -+ __set_bit(j, chip->bbt); -+ } -+ } -+ -+#if defined(CONFIG_MTD_DEBUG) && (MTD_DEBUG_LEVEL3 <= CONFIG_MTD_DEBUG_VERBOSE) -+ printk("%s: Bad Block Table:", chip->name); -+ for (j = 0; j < chip->device->blocks; j++) { -+ if ((j % 64) == 0) { -+ printk("\n%s: block %03x: ", chip->name, j); -+ } -+ printk("%c", test_bit(j, chip->bbt) ? 'X' : '.'); -+ } -+ printk("\n%s: Bad Block Numbers: ", chip->name); -+ for (j = 0; j < chip->device->blocks; j++) { -+ if (test_bit(j, chip->bbt)) { -+ printk("%x ", j); -+ } -+ } -+ printk("\n"); -+#endif -+ -+ return 0; -+} -+ -+#ifndef MODULE -+/* -+ * Called at boot time: -+ * -+ * nand_spi_er=read_only -+ * if read_only specified then do not unlock device -+ */ -+static int __init nand_spi_er_setup(char *str) -+{ -+ if (str && (strncasecmp(str, "read_only", 9) == 0)) { -+ read_only = 1; -+ } -+ return 0; -+} -+ -+__setup("nand_spi_er=", nand_spi_er_setup); -+#endif -+ -+/* -+ * nand_spi_er_probe -+ * Detect and initialize nand_spi_er device. -+ */ -+static int __devinit nand_spi_er_probe(struct spi_device *spi) -+{ -+ uint8_t txbuf[3]; -+ uint8_t rxbuf[2]; -+ int i; -+ int res; -+ size_t bbt_bytes; -+ struct nand_spi_er *chip; -+ const struct nand_spi_er_device *device; -+ -+ res = spi_setup(spi); -+ if (res) { -+ return res; -+ } -+ -+ /* -+ * Reset -+ */ -+ for (i = 0; i < 2; i++) { -+ txbuf[0] = 0xFF; -+ res = spi_write(spi, txbuf, 1); -+ if (res) { -+ return res; -+ } -+ udelay(250); -+ } -+ udelay(1000); -+ -+ /* -+ * Read ID -+ */ -+ txbuf[0] = 0x9F; -+ txbuf[1] = 0x00; -+ res = spi_write_then_read(spi, txbuf, 2, rxbuf, 2); -+ if (res) { -+ return res; -+ } -+ -+ device = nand_spi_er_devices; -+ for (i = 0; i < ARRAY_SIZE(nand_spi_er_devices); i++) { -+ if ((device->id0 == rxbuf[0]) && (device->id1 == rxbuf[1])) { -+ break; -+ } -+ device++; -+ } -+ if (i == ARRAY_SIZE(nand_spi_er_devices)) { -+ return -ENODEV; -+ } -+ -+ /* -+ * Initialize our chip structure -+ */ -+ bbt_bytes = DIV_ROUND_UP(device->blocks, BITS_PER_BYTE); -+ chip = kzalloc(sizeof(struct nand_spi_er) + bbt_bytes, GFP_KERNEL); -+ if (!chip) { -+ return -ENOMEM; -+ } -+ snprintf(chip->name, sizeof(chip->name), "%s.%d.%d", device->name, spi->master->bus_num, spi->chip_select); -+ -+ chip->spi = spi; -+ chip->device = device; -+ chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; -+ -+ mutex_init(&chip->lock); -+ -+ chip->mtd.type = MTD_NANDFLASH; -+ chip->mtd.flags = MTD_WRITEABLE; -+ -+ /* -+ * #blocks * block size * n blocks -+ */ -+ chip->mtd.size = device->blocks * device->pages_per_block * device->page_size; -+ chip->mtd.erasesize = device->erase_size; -+ -+ /* -+ * 1 page, optionally we can support partial write (512) -+ */ -+ chip->mtd.writesize = device->write_size; -+ chip->mtd.name = device->name; -+ chip->mtd.erase = nand_spi_er_erase; -+ chip->mtd.read = nand_spi_er_read; -+ chip->mtd.write = nand_spi_er_write; -+ chip->mtd.block_isbad = nand_spi_er_isbad; -+ chip->mtd.block_markbad = nand_spi_er_markbad; -+ chip->mtd.priv = chip; -+ -+ /* -+ * Cache the bad block table -+ */ -+ res = nand_spi_er_read_bbt(chip); -+ if (res) { -+ kfree(chip); -+ return res; -+ } -+ -+ /* -+ * Un/lock the chip -+ */ -+ txbuf[0] = 0x1F; -+ txbuf[1] = 0xA0; -+ if (read_only) { -+ txbuf[2] = 0x38; -+ } else { -+ txbuf[2] = 0x00; -+ } -+ res = spi_write(spi, txbuf, 3); -+ if (res) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: failed lock operation res=%d\n", chip->name, res); -+ mutex_unlock(&chip->lock); -+ return res; -+ } -+ -+ spi_set_drvdata(spi, chip); -+ -+ printk(KERN_INFO "%s: added device %s size: %u KBytes %u bad blocks %s\n", spi->dev.bus_id, chip->mtd.name, DIV_ROUND_UP(chip->mtd.size, 1024), chip->nbb, read_only ? "[read only]" : ""); -+ return add_mtd_device(&chip->mtd); -+} -+ -+/* -+ * nand_spi_er_remove -+ */ -+static int __devexit nand_spi_er_remove(struct spi_device *spi) -+{ -+ struct nand_spi_er *chip = spi_get_drvdata(spi); -+ int status = 0; -+ -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", spi->dev.bus_id); -+ status = del_mtd_device(&chip->mtd); -+ if (status == 0) -+ kfree(chip); -+ return status; -+} -+ -+static struct spi_driver nand_spi_er_driver = { -+ .driver = { -+ .name = "nand-spi-er", -+ .bus = &spi_bus_type, -+ .owner = THIS_MODULE, -+ }, -+ -+ .probe = nand_spi_er_probe, -+ .remove = __devexit_p(nand_spi_er_remove), -+ -+ /* FIXME: investigate suspend and resume... */ -+}; -+ -+/* -+ * nand_spi_er_init -+ */ -+static int __init nand_spi_er_init(void) -+{ -+ return spi_register_driver(&nand_spi_er_driver); -+} -+module_init(nand_spi_er_init); -+ -+/* -+ * nand_spi_er_exit -+ */ -+static void __exit nand_spi_er_exit(void) -+{ -+ spi_unregister_driver(&nand_spi_er_driver); -+} -+module_exit(nand_spi_er_exit); -+ -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Patrick Tjin"); -+MODULE_DESCRIPTION("MTD nand_spi_er driver"); ---- /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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#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 %lld\n", -+ dev_name(&flash->plt_dev->dev), __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", -+ dev_name(&flash->plt_dev->dev), __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", -+ dev_name(&flash->plt_dev->dev), __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", -+ dev_name(&spi->dev), 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 = dev_name(&spi->dev); -+ -+ 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 (%lld Kbytes)\n", info->name, -+ flash->mtd.size / 1024); -+ -+ DEBUG(MTD_DEBUG_LEVEL2, -+ "mtd .name = %s, .size = 0x%.8llx (%lluMiB) " -+ ".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%.8llx, " -+ ".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%.8llx, " -+ ".size = 0x%.8llx (%lluKiB) }\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"); ---- /dev/null -+++ b/drivers/mtd/devices/ubi32-nand-spi-er.c -@@ -0,0 +1,1188 @@ -+/* -+ * Micron SPI-ER NAND Flash Memory -+ * This code uses the built in Ubicom flash controller -+ * -+ * (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 . -+*/ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define DRIVER_NAME "ubi32-nand-spi-er" -+#define UBI32_NAND_SPI_ER_BLOCK_FROM_ROW(row) (row >> 6) -+ -+#define UBI32_NAND_SPI_ER_STATUS_P_FAIL (1 << 3) -+#define UBI32_NAND_SPI_ER_STATUS_E_FAIL (1 << 2) -+#define UBI32_NAND_SPI_ER_STATUS_OIP (1 << 0) -+ -+#define UBI32_NAND_SPI_ER_LAST_ROW_INVALID 0xFFFFFFFF -+#define UBI32_NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET 0x08 -+ -+struct ubi32_nand_spi_er_device { -+ const char *name; -+ -+ uint16_t id; -+ -+ unsigned int blocks; -+ unsigned int pages_per_block; -+ unsigned int page_size; -+ unsigned int write_size; -+ unsigned int erase_size; -+}; -+ -+struct ubi32_nand_spi_er { -+ char name[24]; -+ -+ const struct ubi32_nand_spi_er_device *device; -+ -+ struct mutex lock; -+ struct platform_device *pdev; -+ -+ struct mtd_info mtd; -+ -+ unsigned int last_row; /* the last row we fetched */ -+ -+ /* -+ * Bad block table (MUST be last in strcuture) -+ */ -+ unsigned long nbb; -+ unsigned long bbt[0]; -+}; -+ -+/* -+ * Chip supports a write_size of 512, but we cannot do partial -+ * page with command 0x84. -+ * -+ * We need to use command 0x84 because we cannot fill the FIFO fast -+ * enough to transfer the whole 512 bytes at a time. (maybe through -+ * OCM?) -+ */ -+const struct ubi32_nand_spi_er_device ubi32_nand_spi_er_devices[] = { -+ { -+ name: "MT29F1G01ZDC", -+ id: 0x2C12, -+ blocks: 1024, -+ pages_per_block: 64, -+ page_size: 2048, -+ write_size: 2048, -+ erase_size: 64 * 2048, -+ }, -+ { -+ name: "MT29F1G01ZDC", -+ id: 0x2C13, -+ blocks: 1024, -+ pages_per_block: 64, -+ page_size: 2048, -+ write_size: 2048, -+ erase_size: 64 * 2048, -+ }, -+}; -+ -+static int read_only = 0; -+module_param(read_only, int, 0); -+MODULE_PARM_DESC(read_only, "Leave device locked"); -+ -+/* -+ * Ubicom32 FLASH Command Set -+ */ -+#define FLASH_PORT RA -+ -+#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 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) \ -+ : "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) \ -+ : "cc" \ -+ ); -+ -+#define FLASH_COMMAND_EXEC(io) \ -+ FLASH_COMMAND_KICK_OFF(io) \ -+ FLASH_COMMAND_WAIT_FOR_COMPLETION(io) -+ -+/* -+ * ubi32_nand_spi_er_get_feature -+ * Get Feature register -+ */ -+static uint8_t ubi32_nand_spi_er_get_feature(uint32_t reg) -+{ -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ -+ /* -+ * Note that this will produce the sequence: -+ * SI [0F][REG][00][00] -+ * SO ---------[SR][SR][SR] -+ * Since the flash controller can only output 24 bits of address, this is -+ * ok for this command since the data will just repeat as long as the CS -+ * is asserted and the clock is running. -+ */ -+ io->ctl1 &= ~IO_XFL_CTL1_MASK; -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_RD) | IO_XFL_CTL1_FC_DATA(1) | -+ IO_XFL_CTL1_FC_ADDR; -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(0x0F) | IO_XFL_CTL2_FC_ADDR(reg << 16); -+ FLASH_COMMAND_EXEC(io); -+ -+ return io->status1 & 0xFF; -+} -+ -+/* -+ * ubi32_nand_spi_er_write_buf -+ * writes a buffer to the bus -+ * -+ * Writes 511 + 1 bytes to the bus, we have to stuff one data byte into the address. -+ */ -+static void ubi32_nand_spi_er_write_buf(const uint8_t *buf, uint32_t col) -+{ -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ uint32_t tmp; -+ -+ asm volatile ( -+ " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_PORTX_INT_FIFO_TX_RESET)") \n\t" -+ " pipe_flush 0 \n\t" -+ : -+ : [port] "a" (FLASH_PORT) -+ : "cc" -+ ); -+ -+ /* -+ * Write the data into the cache -+ */ -+ io->ctl1 &= ~IO_XFL_CTL1_MASK; -+#ifdef SUPPORT_512_FIFO -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | IO_XFL_CTL1_FC_DATA(511) | -+#endif -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | IO_XFL_CTL1_FC_DATA(31) | -+ IO_XFL_CTL1_FC_ADDR; -+ -+ /* -+ * Construct the address with the first byte of data -+ */ -+ tmp = (col << 8) | *buf++; -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(0x84) | IO_XFL_CTL2_FC_ADDR(tmp); -+ -+ asm volatile ( -+ -+ /* -+ * Move 32 bytes -+ * -+ * The first word needs to be [11][22][33][33] to work around a flash -+ * controller bug. -+ */ -+ " move.2 %[tmp], (%[data])2++ \n\t" -+ " shmrg.1 %[tmp], (%[data]), %[tmp] \n\t" -+ " shmrg.1 %[tmp], (%[data])1++, %[tmp] \n\t" -+ " move.4 "D(IO_TX_FIFO)"(%[port]), %[tmp] \n\t" -+ -+ /* -+ * We're aligned again! -+ */ -+ " .rept 7 \n\t" -+ " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" -+ " .endr \n\t" -+ -+ /* -+ * Kick off the flash command -+ */ -+ " bset "D(IO_INT_CLR)"(%[port]), #0, #%%bit("D(IO_XFL_INT_DONE)") \n\t" -+ " jmpt.t .+4 \n\t" -+ " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_XFL_INT_START)") \n\t" -+ -+#ifdef SUPPORT_512_FIFO -+ /* -+ * Fill the remaining 120 words as space becomes available -+ */ -+ "1: \n\t" -+ " cmpi "D(IO_FIFO_LEVEL)"(%[port]), #4 \n\t" -+ " jmpgt.s.t 1b \n\t" -+ " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" -+ " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" -+ " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" -+ " move.4 "D(IO_TX_FIFO)"(%[port]), (%[data])4++ \n\t" -+ " add.4 %[cnt], #-4, %[cnt] \n\t" -+ " jmpgt.t 1b \n\t" -+#endif -+ /* -+ * Wait for the transaction to finish -+ */ -+ " btst "D(IO_INT_STATUS)"(%[port]), #%%bit("D(IO_XFL_INT_DONE)") \n\t" -+ " jmpeq.f .-4 \n\t" -+ -+ : [tmp] "=&d" (tmp), -+ [data] "+&a" (buf) -+ : [column] "d" (col), -+ [port] "a" (FLASH_PORT), -+ [cnt] "d" (120) // see above comment -+ : "cc" -+ ); -+} -+ -+/* -+ * ubi32_nand_spi_er_send_rd_addr -+ * perform FC_RD: CMD + address -+ */ -+static void ubi32_nand_spi_er_send_rd_addr(uint8_t command, uint32_t address) -+{ -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ -+ 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_ADDR; -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(command) | IO_XFL_CTL2_FC_ADDR(address); -+ FLASH_COMMAND_EXEC(io); -+} -+ -+/* -+ * ubi32_nand_spi_er_send_cmd_addr -+ * perform FC_(xxx): CMD + address -+ */ -+static void ubi32_nand_spi_er_send_cmd_addr(uint8_t command, uint32_t address) -+{ -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ -+ 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(command) | IO_XFL_CTL2_FC_ADDR(address); -+ FLASH_COMMAND_EXEC(io); -+} -+ -+/* -+ * ubi32_nand_spi_er_write_disable -+ * clear the write enable bit -+ */ -+static void ubi32_nand_spi_er_write_disable(void) -+{ -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ -+ io->ctl1 &= ~IO_XFL_CTL1_MASK; -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD); -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(0x04); -+ FLASH_COMMAND_EXEC(io); -+} -+ -+/* -+ * ubi32_nand_spi_er_write_enable -+ * set the write enable bit -+ */ -+static void ubi32_nand_spi_er_write_enable(void) -+{ -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ -+ io->ctl1 &= ~IO_XFL_CTL1_MASK; -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD); -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(0x06); -+ FLASH_COMMAND_EXEC(io); -+} -+ -+/* -+ * ubi32_nand_spi_er_busywait -+ * Wait until the chip is not busy -+ */ -+static uint8_t ubi32_nand_spi_er_busywait(void) -+{ -+ int i; -+ uint8_t data; -+ -+ /* -+ * tRD is 100us, so don't delay too long, however, tERS is -+ * 10ms so you'd better loop enough. -+ */ -+ for (i = 0; i < 200; i++) { -+ data = ubi32_nand_spi_er_get_feature(0xC0); -+ if (!(data & UBI32_NAND_SPI_ER_STATUS_OIP)) { -+ break; -+ } -+ -+ udelay(50); -+ } -+ -+ return data; -+} -+ -+/* -+ * ubi32_nand_spi_er_erase -+ * Erase a block, parameters must be block aligned -+ */ -+static int ubi32_nand_spi_er_erase(struct mtd_info *mtd, struct erase_info *instr) -+{ -+ struct ubi32_nand_spi_er *chip = mtd->priv; -+ int res; -+ -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: erase addr:%x len:%x\n", chip->name, instr->addr, instr->len); -+ -+ if ((instr->addr + instr->len) > mtd->size) { -+ return -EINVAL; -+ } -+ -+ if (instr->addr & (chip->device->erase_size - 1)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: erase address is not aligned %x\n", chip->name, instr->addr); -+ return -EINVAL; -+ } -+ -+ if (instr->len & (chip->device->erase_size - 1)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: erase len is not aligned %x\n", chip->name, instr->len); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&chip->lock); -+ chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; -+ -+ while (instr->len) { -+ uint32_t block = instr->addr >> 17; -+ uint32_t row = block << 6; -+ uint8_t stat; -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: block erase row:%x block:%x addr:%x rem:%x\n", chip->name, row, block, instr->addr, instr->len); -+ -+ /* -+ * Test for bad block -+ */ -+ if (test_bit(block, chip->bbt)) { -+ instr->fail_addr = block << 17; -+ instr->state = MTD_ERASE_FAILED; -+ res = -EBADMSG; -+ goto done; -+ } -+ -+ ubi32_nand_spi_er_write_enable(); -+ -+ /* -+ * Block erase -+ */ -+ ubi32_nand_spi_er_send_cmd_addr(0xD8, row); -+ -+ /* -+ * Wait -+ */ -+ stat = ubi32_nand_spi_er_busywait(); -+ if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { -+ instr->fail_addr = block << 17; -+ instr->state = MTD_ERASE_FAILED; -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); -+ -+ /* -+ * Chip is stuck? -+ */ -+ res = -EIO; -+ goto done; -+ } -+ -+ /* -+ * Check the status register -+ */ -+ if (stat & UBI32_NAND_SPI_ER_STATUS_E_FAIL) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: E_FAIL signalled (%02x)\n", chip->name, stat); -+ instr->fail_addr = block << 17; -+ instr->state = MTD_ERASE_FAILED; -+ goto done; -+ } -+ -+ /* -+ * Next -+ */ -+ block++; -+ instr->len -= chip->device->erase_size; -+ instr->addr += chip->device->erase_size; -+ } -+ -+ instr->state = MTD_ERASE_DONE; -+ -+ mutex_unlock(&chip->lock); -+ return 0; -+ -+done: -+ ubi32_nand_spi_er_write_disable(); -+ -+ mutex_unlock(&chip->lock); -+ -+ mtd_erase_callback(instr); -+ return 0; -+} -+ -+/* -+ * ubi32_nand_spi_er_read -+ * -+ * return -EUCLEAN: ecc error recovered -+ * return -EBADMSG: ecc error not recovered -+*/ -+static int ubi32_nand_spi_er_read(struct mtd_info *mtd, loff_t from, size_t len, -+ size_t *retlen, u_char *buf) -+{ -+ struct ubi32_nand_spi_er *chip = mtd->priv; -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ -+ uint32_t row; -+ uint32_t column; -+ int retval = 0; -+ uint32_t *pbuf = (uint32_t *)buf; -+ -+ *retlen = 0; -+ DEBUG(MTD_DEBUG_LEVEL2, "%s: read block from %llx len %d into %p\n", chip->name, from, len, buf); -+ -+ /* -+ * buf should be aligned -+ */ -+ if ((uint32_t)buf & 0x03) { -+ return -EINVAL; -+ } -+ -+ /* -+ * Zero length reads, nothing to do -+ */ -+ if (len == 0) { -+ return 0; -+ } -+ -+ /* -+ * Reject reads which go over the end of the flash -+ */ -+ if ((from + len) > mtd->size) { -+ return -EINVAL; -+ } -+ -+ /* -+ * Get the row and column address to start at -+ */ -+ row = from >> 11; -+ column = from & 0x7FF; -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: row=%x %d column=%x %d last_row=%x %d\n", chip->name, row, row, column, column, chip->last_row, chip->last_row); -+ -+ /* -+ * Read the data from the chip -+ */ -+ mutex_lock(&chip->lock); -+ while (len) { -+ uint8_t stat; -+ size_t toread; -+ int i; -+ int tmp; -+ -+ /* -+ * Figure out how much to read -+ * -+ * If we are reading from the middle of a page then the most we -+ * can read is to the end of the page -+ */ -+ toread = len; -+ if (toread > (chip->device->page_size - column)) { -+ toread = chip->device->page_size - column; -+ } -+ -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: buf=%p toread=%x row=%x column=%x last_row=%x\n", chip->name, pbuf, toread, row, column, chip->last_row); -+ -+ if (chip->last_row != row) { -+ /* -+ * Check if the block is bad -+ */ -+ if (test_bit(UBI32_NAND_SPI_ER_BLOCK_FROM_ROW(row), chip->bbt)) { -+ mutex_unlock(&chip->lock); -+ return -EBADMSG; -+ } -+ -+ /* -+ * Load the appropriate page -+ */ -+ ubi32_nand_spi_er_send_cmd_addr(0x13, row); -+ -+ /* -+ * Wait -+ */ -+ stat = ubi32_nand_spi_er_busywait(); -+ if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); -+ -+ /* -+ * Chip is stuck? -+ */ -+ mutex_unlock(&chip->lock); -+ return -EIO; -+ } -+ -+ /* -+ * Check the ECC bits -+ */ -+ stat >>= 4; -+ if (stat == 1) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: ECC recovered, row=%x\n", chip->name, row); -+ retval = -EUCLEAN; -+ } -+ if (stat == 2) { -+ DEBUG(MTD_DEBUG_LEVEL0, "%s: failed ECC, row=%x\n", chip->name, row); -+ chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; -+ mutex_unlock(&chip->lock); -+ return -EBADMSG; -+ } -+ -+ } -+ -+ chip->last_row = row; -+ -+ /* -+ * Read out the data: -+ * We can always read a little too much since there is the -+ * OOB after byte addr 2047. The most we'll overread is 3 bytes. -+ */ -+ if (((uint32_t)pbuf & 0x03) == 0) { -+ /* -+ * Aligned read -+ */ -+ tmp = toread & (~0x03); -+ for (i = 0; i < tmp; i += 4) { -+ ubi32_nand_spi_er_send_rd_addr(0x03, column << 8); -+ *pbuf++ = io->status1; -+ column += 4; -+ } -+ } else { -+ /* -+ * Unaligned read -+ */ -+ tmp = toread & (~0x03); -+ for (i = 0; i < tmp; i += 4) { -+ ubi32_nand_spi_er_send_rd_addr(0x03, column << 8); -+ memcpy(pbuf, &io->status1, 4); -+ column += 4; -+ } -+ } -+ -+ /* -+ * Fill in any single bytes -+ */ -+ tmp = toread & 0x03; -+ if (tmp) { -+ uint8_t *bbuf = pbuf; -+ uint32_t val; -+ ubi32_nand_spi_er_send_rd_addr(0x03, column << 8); -+ val = io->status1; -+ for (i = 0; i < tmp; i++) { -+ *bbuf++ = val >> 24; -+ val <<= 8; -+ } -+ } -+ -+ len -= toread; -+ *retlen += toread; -+ -+ /* -+ * For the next page, increment the row and always start at column 0 -+ */ -+ column = 0; -+ row++; -+ } -+ -+ mutex_unlock(&chip->lock); -+ return retval; -+} -+ -+/* -+ * ubi32_nand_spi_er_write -+ */ -+#define WRITE_NOT_ALIGNED(x) ((x & (device->write_size - 1)) != 0) -+static int ubi32_nand_spi_er_write(struct mtd_info *mtd, loff_t to, size_t len, -+ size_t *retlen, const u_char *buf) -+{ -+ struct ubi32_nand_spi_er *chip = mtd->priv; -+ const struct ubi32_nand_spi_er_device *device = chip->device; -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ uint32_t row; -+ uint32_t col; -+ int res = 0; -+ size_t towrite; -+ -+ DEBUG(MTD_DEBUG_LEVEL2, "%s: write block to %llx len %d from %p\n", chip->name, to, len, buf); -+ -+ *retlen = 0; -+ -+ /* -+ * nothing to write -+ */ -+ if (!len) { -+ return 0; -+ } -+ -+ /* -+ * Reject writes which go over the end of the flash -+ */ -+ if ((to + len) > mtd->size) { -+ return -EINVAL; -+ } -+ -+ /* -+ * buf should be aligned to 16 bits -+ */ -+ if ((uint32_t)buf & 0x01) { -+ return -EINVAL; -+ } -+ -+ /* -+ * Check to see if everything is page aligned -+ */ -+ if (WRITE_NOT_ALIGNED(to) || WRITE_NOT_ALIGNED(len)) { -+ printk(KERN_NOTICE "ubi32_nand_spi_er_write: Attempt to write non page aligned data\n"); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&chip->lock); -+ -+ io->ctl0 |= IO_XFL_CTL0_MCB_LOCK; -+ -+ chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; -+ -+ /* -+ * If the first write is a partial write then write at most the number of -+ * bytes to get us page aligned and then the remainder will be -+ * page aligned. The last bit may be a partial page as well. -+ */ -+ col = to & (device->page_size - 1); -+ towrite = device->page_size - col; -+ if (towrite > len) { -+ towrite = len; -+ } -+ -+ /* -+ * Write the data -+ */ -+ row = to >> 11; -+ while (len) { -+ uint8_t stat; -+ uint32_t my_towrite; -+ -+ DEBUG(MTD_DEBUG_LEVEL3, "%s: write %p to row:%x col:%x len:%x rem:%x\n", chip->name, buf, row, col, towrite, len); -+ -+ ubi32_nand_spi_er_write_enable(); -+ -+ /* -+ * Move the data into the cache -+ */ -+ my_towrite = towrite; -+ while (my_towrite) { -+ uint32_t len = my_towrite; -+ if (len > 32) { -+ len = 32; -+ } -+ -+ ubi32_nand_spi_er_write_buf(buf, col); -+ buf += len; -+ col += len; -+ my_towrite -= len; -+ } -+ -+ /* -+ * Program execute -+ */ -+ ubi32_nand_spi_er_send_cmd_addr(0x10, row); -+ -+ /* -+ * Wait -+ */ -+ stat = ubi32_nand_spi_er_busywait(); -+ if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); -+ -+ /* -+ * Chip is stuck? -+ */ -+ res = -EIO; -+ goto done; -+ } -+ -+ if (stat & (1 << 3)) { -+ res = -EBADMSG; -+ goto done; -+ } -+ -+ row++; -+ len -= towrite; -+ *retlen += towrite; -+ -+ /* -+ * At this point, we are always page aligned so start at column 0. -+ * Note we may not have a full page to write at the end, hence the -+ * check if towrite > len. -+ */ -+ col = 0; -+ towrite = device->page_size; -+ if (towrite > len) { -+ towrite = len; -+ } -+ } -+ -+ io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; -+ -+ mutex_unlock(&chip->lock); -+ return res; -+ -+done: -+ ubi32_nand_spi_er_write_disable(); -+ -+ io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; -+ -+ mutex_unlock(&chip->lock); -+ -+ return res; -+} -+ -+/* -+ * ubi32_nand_spi_er_isbad -+ */ -+static int ubi32_nand_spi_er_isbad(struct mtd_info *mtd, loff_t ofs) -+{ -+ struct ubi32_nand_spi_er *chip = mtd->priv; -+ uint32_t block; -+ -+ if (ofs & (chip->device->erase_size - 1)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); -+ return -EINVAL; -+ } -+ -+ block = ofs >> 17; -+ -+ return test_bit(block, chip->bbt); -+} -+ -+/* -+ * ubi32_nand_spi_er_markbad -+ */ -+static int ubi32_nand_spi_er_markbad(struct mtd_info *mtd, loff_t ofs) -+{ -+ struct ubi32_nand_spi_er *chip = mtd->priv; -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ uint32_t block; -+ uint32_t row; -+ int res = 0; -+ uint8_t stat; -+ -+ if (ofs & (chip->device->erase_size - 1)) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); -+ return -EINVAL; -+ } -+ -+ block = ofs >> 17; -+ -+ /* -+ * If it's already marked bad, no need to mark it -+ */ -+ if (test_bit(block, chip->bbt)) { -+ return 0; -+ } -+ -+ /* -+ * Mark it in our cache -+ */ -+ __set_bit(block, chip->bbt); -+ -+ /* -+ * Write the user bad block mark. If it fails, then we really -+ * can't do anything about it. -+ */ -+ mutex_lock(&chip->lock); -+ chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; -+ -+ ubi32_nand_spi_er_write_enable(); -+ -+ /* -+ * Write the mark -+ */ -+ io->ctl0 |= IO_XFL_CTL0_MCB_LOCK; -+ io->ctl1 &= ~IO_XFL_CTL1_MASK; -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | IO_XFL_CTL1_FC_DATA(6); -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(0x84); -+ -+ asm volatile ( -+ " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_PORTX_INT_FIFO_TX_RESET)") \n\t" -+ " pipe_flush 0 \n\t" -+ -+ /* -+ * Move the data into the FIFO -+ */ -+ " move.4 "D(IO_TX_FIFO)"(%[port]), %[word1] \n\t" -+ " move.4 "D(IO_TX_FIFO)"(%[port]), %[word2] \n\t" -+ -+ /* -+ * Kick off the flash command -+ */ -+ " bset "D(IO_INT_CLR)"(%[port]), #0, #%%bit("D(IO_XFL_INT_DONE)") \n\t" -+ " jmpt.t .+4 \n\t" -+ " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_XFL_INT_START)") \n\t" -+ -+ /* -+ * Wait for the transaction to finish -+ */ -+ " btst "D(IO_INT_STATUS)"(%[port]), #%%bit("D(IO_XFL_INT_DONE)") \n\t" -+ " jmpeq.f .-4 \n\t" -+ -+ : -+ : [word1] "d" (0x0800dead | (UBI32_NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET << 16)), -+ [word2] "d" (0xbeef0000), -+ [port] "a" (FLASH_PORT) -+ : "cc" -+ ); -+ -+ io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; -+ -+ /* -+ * Program execute -+ */ -+ row = block << 6; -+ ubi32_nand_spi_er_send_cmd_addr(0x10, row); -+ -+ /* -+ * Wait -+ */ -+ stat = ubi32_nand_spi_er_busywait(); -+ if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); -+ -+ /* -+ * Chip is stuck? -+ */ -+ res = -EIO; -+ goto done; -+ } -+ -+ if (stat & (1 << 3)) { -+ res = -EBADMSG; -+ } -+ -+done: -+ ubi32_nand_spi_er_write_disable(); -+ -+ mutex_unlock(&chip->lock); -+ -+ return res; -+} -+ -+/* -+ * ubi32_nand_spi_er_read_bbt -+ */ -+static int ubi32_nand_spi_er_read_bbt(struct ubi32_nand_spi_er *chip) -+{ -+ int j; -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ -+ for (j = 0; j < chip->device->blocks; j++) { -+ unsigned short row = j << 6; -+ uint8_t stat; -+ -+ /* -+ * Read Page -+ */ -+ ubi32_nand_spi_er_send_cmd_addr(0x13, row); -+ -+ /* -+ * Wait -+ */ -+ stat = ubi32_nand_spi_er_busywait(); -+ if (stat & UBI32_NAND_SPI_ER_STATUS_OIP) { -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive stat=%02x\n", chip->name, stat); -+ -+ /* -+ * Chip is stuck? -+ */ -+ return -EIO; -+ } -+ -+ /* -+ * Check factory bad block mark -+ */ -+ ubi32_nand_spi_er_send_rd_addr(0x03, 0x080000); -+ -+ if ((io->status1 >> 24) != 0xFF) { -+ chip->nbb++; -+ __set_bit(j, chip->bbt); -+ continue; -+ } -+ -+ ubi32_nand_spi_er_send_rd_addr(0x03, 0x080000 | (UBI32_NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET << 8)); -+ if (io->status1 == 0xdeadbeef) { -+ chip->nbb++; -+ __set_bit(j, chip->bbt); -+ } -+ } -+ -+#if defined(CONFIG_MTD_DEBUG) && (MTD_DEBUG_LEVEL3 <= CONFIG_MTD_DEBUG_VERBOSE) -+ printk("%s: Bad Block Table:", chip->name); -+ for (j = 0; j < chip->device->blocks; j++) { -+ if ((j % 64) == 0) { -+ printk("\n%s: block %03x: ", chip->name, j); -+ } -+ printk("%c", test_bit(j, chip->bbt) ? 'X' : '.'); -+ } -+ printk("\n%s: Bad Block Numbers: ", chip->name); -+ for (j = 0; j < chip->device->blocks; j++) { -+ if (test_bit(j, chip->bbt)) { -+ printk("%x ", j); -+ } -+ } -+ printk("\n"); -+#endif -+ -+ return 0; -+} -+ -+#ifndef MODULE -+/* -+ * Called at boot time: -+ * -+ * ubi32_nand_spi_er=read_only -+ * if read_only specified then do not unlock device -+ */ -+static int __init ubi32_nand_spi_er_setup(char *str) -+{ -+ if (str && (strncasecmp(str, "read_only", 9) == 0)) { -+ read_only = 1; -+ } -+ return 0; -+} -+ -+__setup("ubi32_nand_spi_er=", ubi32_nand_spi_er_setup); -+#endif -+ -+/* -+ * ubi32_nand_spi_er_probe -+ * Detect and initialize ubi32_nand_spi_er device. -+ */ -+static int __devinit ubi32_nand_spi_er_probe(struct platform_device *pdev) -+{ -+ uint32_t i; -+ uint32_t id; -+ int res; -+ size_t bbt_bytes; -+ struct ubi32_nand_spi_er *chip; -+ const struct ubi32_nand_spi_er_device *device; -+ struct ubicom32_io_port *io = (struct ubicom32_io_port *)FLASH_PORT; -+ -+ /* -+ * Reset -+ */ -+ for (i = 0; i < 2; i++) { -+ io->ctl1 &= ~IO_XFL_CTL1_MASK; -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_CMD); -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(0xFF); -+ FLASH_COMMAND_EXEC(io); -+ udelay(250); -+ } -+ udelay(1000); -+ -+ /* -+ * Read out ID -+ */ -+ io->ctl1 &= ~IO_XFL_CTL1_MASK; -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_RD) | IO_XFL_CTL1_FC_DATA(2) | -+ IO_XFL_CTL1_FC_ADDR; -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(0x9F); -+ FLASH_COMMAND_EXEC(io); -+ -+ id = io->status1 >> 16; -+ device = ubi32_nand_spi_er_devices; -+ for (i = 0; i < ARRAY_SIZE(ubi32_nand_spi_er_devices); i++) { -+ if (device->id == id) { -+ break; -+ } -+ device++; -+ } -+ if (i == ARRAY_SIZE(ubi32_nand_spi_er_devices)) { -+ return -ENODEV; -+ } -+ -+ /* -+ * Initialize our chip structure -+ */ -+ bbt_bytes = DIV_ROUND_UP(device->blocks, BITS_PER_BYTE); -+ chip = kzalloc(sizeof(struct ubi32_nand_spi_er) + bbt_bytes, GFP_KERNEL); -+ if (!chip) { -+ return -ENOMEM; -+ } -+ snprintf(chip->name, sizeof(chip->name), "%s", device->name); -+ -+ chip->device = device; -+ chip->last_row = UBI32_NAND_SPI_ER_LAST_ROW_INVALID; -+ -+ mutex_init(&chip->lock); -+ -+ chip->mtd.type = MTD_NANDFLASH; -+ chip->mtd.flags = MTD_WRITEABLE; -+ -+ /* -+ * #blocks * block size * n blocks -+ */ -+ chip->mtd.size = device->blocks * device->pages_per_block * device->page_size; -+ chip->mtd.erasesize = device->erase_size; -+ -+ /* -+ * 1 page, optionally we can support partial write (512) -+ */ -+ chip->mtd.writesize = device->write_size; -+ chip->mtd.name = device->name; -+ chip->mtd.erase = ubi32_nand_spi_er_erase; -+ chip->mtd.read = ubi32_nand_spi_er_read; -+ chip->mtd.write = ubi32_nand_spi_er_write; -+ chip->mtd.block_isbad = ubi32_nand_spi_er_isbad; -+ chip->mtd.block_markbad = ubi32_nand_spi_er_markbad; -+ chip->mtd.priv = chip; -+ -+ /* -+ * Cache the bad block table -+ */ -+ res = ubi32_nand_spi_er_read_bbt(chip); -+ if (res) { -+ kfree(chip); -+ return res; -+ } -+ -+ /* -+ * Un/lock the chip -+ */ -+ io->ctl0 |= IO_XFL_CTL0_MCB_LOCK; -+ io->ctl1 &= ~IO_XFL_CTL1_MASK; -+ io->ctl1 |= IO_XFL_CTL1_FC_INST(FLASH_FC_INST_WR) | IO_XFL_CTL1_FC_DATA(2); -+ io->ctl2 = IO_XFL_CTL2_FC_CMD(0x1F); -+ -+ if (read_only) { -+ i = 0xa0380000; -+ } else { -+ i = 0xa0000000; -+ } -+ asm volatile ( -+ " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_PORTX_INT_FIFO_TX_RESET)") \n\t" -+ " pipe_flush 0 \n\t" -+ -+ /* -+ * Move the data into the FIFO -+ */ -+ " move.4 "D(IO_TX_FIFO)"(%[port]), %[word1] \n\t" -+ -+ /* -+ * Kick off the flash command -+ */ -+ " bset "D(IO_INT_CLR)"(%[port]), #0, #%%bit("D(IO_XFL_INT_DONE)") \n\t" -+ " jmpt.t .+4 \n\t" -+ " bset "D(IO_INT_SET)"(%[port]), #0, #%%bit("D(IO_XFL_INT_START)") \n\t" -+ -+ /* -+ * Wait for the transaction to finish -+ */ -+ " btst "D(IO_INT_STATUS)"(%[port]), #%%bit("D(IO_XFL_INT_DONE)") \n\t" -+ " jmpeq.f .-4 \n\t" -+ -+ : -+ : [word1] "d" (i), -+ [port] "a" (FLASH_PORT) -+ : "cc" -+ ); -+ io->ctl0 &= ~IO_XFL_CTL0_MCB_LOCK; -+ -+ dev_set_drvdata(&pdev->dev, chip); -+ -+ printk(KERN_INFO "%s: added device size: %u KBytes %lu bad blocks %s\n", chip->mtd.name, DIV_ROUND_UP(chip->mtd.size, 1024), chip->nbb, read_only ? "[read only]" : ""); -+ return add_mtd_device(&chip->mtd); -+} -+ -+/* -+ * ubi32_nand_spi_er_remove -+ */ -+static int __devexit ubi32_nand_spi_er_remove(struct platform_device *pdev) -+{ -+ struct ubi32_nand_spi_er *chip = dev_get_drvdata(&pdev->dev); -+ int status; -+ -+ DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", chip->name); -+ -+ status = del_mtd_device(&chip->mtd); -+ if (status == 0) { -+ kfree(chip); -+ } -+ -+ dev_set_drvdata(&pdev->dev, NULL); -+ return status; -+} -+ -+static struct platform_device *ubi32_nand_spi_er_device; -+ -+static struct platform_driver ubi32_nand_spi_er_driver = { -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+ -+ .probe = ubi32_nand_spi_er_probe, -+ .remove = ubi32_nand_spi_er_remove, -+}; -+ -+/* -+ * ubi32_nand_spi_er_init -+ */ -+static int __init ubi32_nand_spi_er_init(void) -+{ -+ int ret; -+ -+ ret = platform_driver_register(&ubi32_nand_spi_er_driver); -+ -+ if (ret) { -+ return ret; -+ } -+ -+ ubi32_nand_spi_er_device = platform_device_alloc(DRIVER_NAME, 0); -+ if (!ubi32_nand_spi_er_device) { -+ return -ENOMEM; -+ } -+ -+ ret = platform_device_add(ubi32_nand_spi_er_device); -+ if (ret) { -+ platform_device_put(ubi32_nand_spi_er_device); -+ platform_driver_unregister(&ubi32_nand_spi_er_driver); -+ } -+ -+ return ret; -+} -+module_init(ubi32_nand_spi_er_init); -+ -+/* -+ * ubi32_nand_spi_er_exit -+ */ -+static void __exit ubi32_nand_spi_er_exit(void) -+{ -+ platform_device_unregister(ubi32_nand_spi_er_device); -+ platform_driver_unregister(&ubi32_nand_spi_er_driver); -+} -+module_exit(ubi32_nand_spi_er_exit); -+ -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Patrick Tjin"); -+MODULE_DESCRIPTION("MTD ubi32_nand_spi_er driver for ubicom32 SPI flash controller."); ---- a/drivers/net/Kconfig -+++ b/drivers/net/Kconfig -@@ -2540,6 +2540,19 @@ 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" -+ depends on UBICOM32 -+ help -+ Gigabit Ethernet support for ubicom32 processors -+ -+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 when possible -+ - endif # NETDEV_1000 - - # ---- a/drivers/net/Makefile -+++ b/drivers/net/Makefile -@@ -272,3 +272,5 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o - obj-$(CONFIG_SFC) += sfc/ - - obj-$(CONFIG_WIMAX) += wimax/ -+ -+obj-$(CONFIG_UBICOM32_GMAC) += ubi32-eth.o ---- /dev/null -+++ b/drivers/net/ubi32-eth.c -@@ -0,0 +1,760 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+/* -+ * ubi32_eth.c -+ * Ethernet driver for Ip5k/Ip7K -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define UBICOM32_USE_NAPI /* define this to use NAPI instead of tasklet */ -+//#define UBICOM32_USE_POLLING /* define this to use polling instead of interrupt */ -+#include "ubi32-eth.h" -+ -+/* -+ * TODO: -+ * mac address from flash -+ * multicast filter -+ * ethtool support -+ * sysfs support -+ * skb->nrfrag support -+ * ioctl -+ * monitor phy status -+ */ -+ -+extern int ubi32_ocm_skbuf_max, ubi32_ocm_skbuf, ubi32_ddr_skbuf; -+static const char *eth_if_name[UBI32_ETH_NUM_OF_DEVICES] = -+ {"eth_lan", "eth_wan"}; -+static struct net_device *ubi32_eth_devices[UBI32_ETH_NUM_OF_DEVICES] = -+ {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'}}; -+ -+#if (defined(CONFIG_ZONE_DMA) && defined(CONFIG_UBICOM32_OCM_FOR_SKB)) -+static inline struct sk_buff *ubi32_alloc_skb_ocm(struct net_device *dev, unsigned int length) -+{ -+ return __dev_alloc_skb(length, GFP_ATOMIC | __GFP_NOWARN | __GFP_NORETRY | GFP_DMA); -+} -+#endif -+ -+static inline struct sk_buff *ubi32_alloc_skb(struct net_device *dev, unsigned int length) -+{ -+ return __dev_alloc_skb(length, GFP_ATOMIC | __GFP_NOWARN); -+} -+ -+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))) { -+ dev->stats.tx_errors++; -+ } else { -+ dev->stats.tx_packets++; -+ dev->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->regs->status & UBI32_ETH_VP_STATUS_TX_Q_FULL)) { -+ spin_lock(&priv->lock); -+ if (priv->regs->status & UBI32_ETH_VP_STATUS_TX_Q_FULL) { -+ priv->regs->status &= ~UBI32_ETH_VP_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; -+ -+ int extra_reserve_adj; -+ int extra_alloc = UBI32_ETH_RESERVE_SPACE + UBI32_ETH_TRASHED_MEMORY; -+ int replenish_cnt, count = 0; -+ int replenish_max = RX_DMA_MAX_QUEUE_SIZE; -+#if (defined(CONFIG_ZONE_DMA) && defined(CONFIG_UBICOM32_OCM_FOR_SKB)) -+ if (likely(dev == ubi32_eth_devices[0])) -+ replenish_max = min(ubi32_ocm_skbuf_max, RX_DMA_MAX_QUEUE_SIZE);; -+#endif -+ -+ 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))) { -+ dev->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; -+ dev->stats.rx_bytes += skb->len; -+ dev->stats.rx_packets++; -+#ifndef UBICOM32_USE_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) { -+#if (defined(CONFIG_ZONE_DMA) && defined(CONFIG_UBICOM32_OCM_FOR_SKB)) -+ /* -+ * black magic for perforamnce: -+ * Try to allocate skb from OCM only for first Ethernet I/F. -+ * Also limit number of RX buffers to 21 due to limited OCM. -+ */ -+ if (likely(dev == ubi32_eth_devices[0])) { -+ do { -+ skb = ubi32_alloc_skb_ocm(dev, RX_BUF_SIZE + extra_alloc); -+ if (!skb) { -+ break; -+ } -+ /* set up dma descriptor */ -+ ubi32_ocm_skbuf++; -+ desc = (struct ubi32_eth_dma_desc *)skb->cb; -+ extra_reserve_adj = -+ ((u32)skb->data + UBI32_ETH_RESERVE_SPACE + ETH_HLEN) & -+ (CACHE_LINE_SIZE - 1); -+ skb_reserve(skb, UBI32_ETH_RESERVE_SPACE - extra_reserve_adj); -+ 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; -+ } while (--replenish_cnt > 0); -+ } -+#endif -+ -+ while (replenish_cnt-- > 0) { -+ skb = ubi32_alloc_skb(dev, RX_BUF_SIZE + extra_alloc); -+ if (!skb) { -+ priv->vp_stats.rx_alloc_err++; -+ break; -+ } -+ /* set up dma descriptor */ -+ ubi32_ddr_skbuf++; -+ desc = (struct ubi32_eth_dma_desc *)skb->cb; -+ extra_reserve_adj = -+ ((u32)skb->data + UBI32_ETH_RESERVE_SPACE + ETH_HLEN) & -+ (CACHE_LINE_SIZE - 1); -+ skb_reserve(skb, UBI32_ETH_RESERVE_SPACE - extra_reserve_adj); -+ 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 UBICOM32_USE_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); -+ -+ if (count < budget) { -+ napi_complete(napi); -+ 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)) { -+ if (napi_reschedule(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, UBI32_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(UBICOM32_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 UBICOM32_USE_NAPI -+ napi_schedule(&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 UBICOM32_USE_NAPI -+ if (napi_schedule_prep(&priv->napi)) { -+ priv->regs->int_mask = 0; -+ __napi_schedule(&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 UBICOM32_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 UBICOM32_USE_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 UBICOM32_USE_POLLING -+ free_irq(dev->irq, dev); -+#endif -+ netif_stop_queue(dev); /* can't transmit any more */ -+#ifdef UBICOM32_USE_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 UBICOM32_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)) { -+ if (!(priv->regs->status & UBI32_ETH_VP_STATUS_TX_Q_FULL)) { -+ spin_lock(&priv->lock); -+ if (!(priv->regs->status & UBI32_ETH_VP_STATUS_TX_Q_FULL)) { -+ priv->regs->status |= UBI32_ETH_VP_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 UBICOM32_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 NETDEV_TX_OK; -+} -+ -+/* -+ * Deal with a transmit timeout. -+ */ -+static void ubi32_eth_tx_timeout (struct net_device *dev) -+{ -+ struct ubi32_eth_private *priv = netdev_priv(dev); -+ dev->stats.tx_errors++; -+ priv->regs->int_mask |= UBI32_ETH_VP_INT_TX; -+#ifndef UBICOM32_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) -+{ -+ struct ubi32_eth_private *priv = netdev_priv(dev); -+ struct mii_ioctl_data *data = if_mii(rq); -+ -+ printk(KERN_INFO "ioctl %s, %d\n", dev->name, cmd); -+ switch (cmd) { -+ case SIOCGMIIPHY: -+ data->phy_id = 0; -+ break; -+ -+ case SIOCGMIIREG: -+ if ((data->reg_num & 0x1F) == MII_BMCR) { -+ /* Make up MII control register value from what we know */ -+ data->val_out = 0x0000 -+ | ((priv->regs->status & UBI32_ETH_VP_STATUS_DUPLEX) -+ ? BMCR_FULLDPLX : 0) -+ | ((priv->regs->status & UBI32_ETH_VP_STATUS_SPEED100) -+ ? BMCR_SPEED100 : 0) -+ | ((priv->regs->status & UBI32_ETH_VP_STATUS_SPEED1000) -+ ? BMCR_SPEED1000 : 0); -+ } else if ((data->reg_num & 0x1F) == MII_BMSR) { -+ /* Make up MII status register value from what we know */ -+ data->val_out = -+ (BMSR_100FULL|BMSR_100HALF|BMSR_10FULL|BMSR_10HALF) -+ | ((priv->regs->status & UBI32_ETH_VP_STATUS_LINK) -+ ? BMSR_LSTATUS : 0); -+ } else { -+ return -EIO; -+ } -+ break; -+ -+ case SIOCSMIIREG: -+ return -EOPNOTSUPP; -+ break; -+ -+ default: -+ return -EOPNOTSUPP; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Return statistics to the caller -+ */ -+static struct net_device_stats *ubi32_eth_get_stats(struct net_device *dev) -+{ -+ return &dev->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 "fail 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; -+ -+ /* -+ * This just fill in some default Ubicom MAC address -+ */ -+ memcpy(dev->dev_addr, mac_addr[i], ETH_ALEN); -+ memset(dev->broadcast, 0xff, 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 UBICOM32_USE_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 feature:0x%lx\n", -+ dev->name, priv->regs, eth_node->dn.sendirq, dev->irq, dev->features); -+ } -+ -+ if (err) { -+ ubi32_eth_cleanup(); -+ return err; -+ } -+ -+ if (!ubi32_eth_devices[0] && !ubi32_eth_devices[1]) { -+ return -ENODEV; -+ } -+ -+#if defined(UBICOM32_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,132 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#ifndef _UBI32_ETH_H -+#define _UBI32_ETH_H -+ -+#include -+ -+#define UBI32_ETH_NUM_OF_DEVICES 2 -+ -+/* -+ * 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 at least one cache line to align Ethernet -+ * and IP header to cache line. -+ * Note that the TIO expects a CACHE_LINE_SIZE - ETH_HLEN aligned Ethernet -+ * header, while satisfies NET_IP_ALIGN (= 2) automatically. -+ * (NET_SKB_PAD is 16, NET_IP_ALIGN is 2, CACHE_LINE_SIZE is 32). -+ * You can add more space by making UBI32_ETH_RESERVE_EXTRA != 0. -+ */ -+#define UBI32_ETH_RESERVE_EXTRA (1 * CACHE_LINE_SIZE) -+#define UBI32_ETH_RESERVE_SPACE (UBI32_ETH_RESERVE_EXTRA + 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 ubi32_eth_vp_stats vp_stats; -+ spinlock_t lock; -+#ifdef UBICOM32_USE_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_SPEED100 (0x1<<1) -+#define UBI32_ETH_VP_STATUS_SPEED1000 (0x1<<2) -+#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_VP_STATUS_TX_Q_FULL (1<<8) -+ -+#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 UBI32_TX_BOUND TX_DMA_RING_SIZE -+#define UBI32_RX_BOUND 64 -+#define UBI32_ETH_NAPI_WEIGHT 64 /* for GigE */ -+#endif ---- a/drivers/net/usb/asix.c -+++ b/drivers/net/usb/asix.c -@@ -319,14 +319,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); -@@ -1125,13 +1144,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 -@@ -328,10 +328,10 @@ static inline void oprofile_end_trace(st - } - - static inline void --__oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, -- unsigned long event, int is_kernel) -+__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); - unsigned long backtrace = oprofile_backtrace_depth; - - /* -@@ -353,7 +353,8 @@ __oprofile_add_ext_sample(unsigned long - void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, - unsigned long event, int is_kernel) - { -- __oprofile_add_ext_sample(pc, regs, event, 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) -@@ -361,7 +362,8 @@ void oprofile_add_sample(struct pt_regs - int is_kernel = !user_mode(regs); - unsigned long pc = profile_pc(regs); - -- __oprofile_add_ext_sample(pc, regs, event, is_kernel); -+ __oprofile_add_ext_sample_cpu(pc, regs, event, -+ is_kernel, smp_processor_id()); - } - - /* ---- a/drivers/pci/Makefile -+++ b/drivers/pci/Makefile -@@ -44,8 +44,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 -@@ -871,6 +871,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 -@@ -77,3 +77,6 @@ obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIA - 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+ -+#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 "); -+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,1172 @@ -+/* -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#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; -+ static int probe_result; -+ -+ /* -+ * We only want to be probed once, we could be probed twice -+ * for example if we are used as a console -+ */ -+ if (probed) { -+ return probe_result; -+ } -+ probed = 1; -+ -+ /* -+ * Extract the TIO name from the setup string -+ */ -+ while (*str) { -+ if (*str == ',') { -+ *str++ = 0; -+ break; -+ } -+ str++; -+ } -+ -+ if (!*str) { -+ probe_result = -EINVAL; -+ return -EINVAL; -+ } -+ -+ uart_node = (struct uarttio_node *)devtree_find_node(utio_ports_param); -+ if (!uart_node) { -+ probe_result = -ENODEV; -+ 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) { -+ ubi32_uarttio_cleanup(); -+ probe_result = ret; -+ return ret; -+ } -+ -+ ubi32_uarttio_uart_driver.nr = uarttio_nports; -+ -+ return 0; -+} -+ -+#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 "); -+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,267 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+ -+#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 -+ -+/* -+ * 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 -@@ -89,4 +89,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 -@@ -5,3 +5,4 @@ obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdr - obj-$(CONFIG_UIO_SMX) += uio_smx.o - obj-$(CONFIG_UIO_AEC) += uio_aec.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,288 @@ -+/* -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#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!! */ ---- 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 -@@ -12,7 +12,7 @@ config USB_MUSB_HDRC - depends on !SUPERH - select TWL4030_USB if MACH_OMAP_3430SDP - select USB_OTG_UTILS -- tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' -+ tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, 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 -@@ -30,6 +30,10 @@ ifeq ($(CONFIG_BF52x),y) - musb_hdrc-objs += blackfin.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 - #endif - -+#ifdef CONFIG_UBICOM32 -+#include -+#include -+extern void ubi32_usb_init(void); -+extern void ubi32_usb_int_clr(void); -+#endif -+ - #include "musb_core.h" - - -@@ -147,8 +154,37 @@ static inline struct musb *dev_to_musb(s - } - - /*-------------------------------------------------------------------------*/ -+#if defined(CONFIG_UBICOM32) -+ -+/* -+ * 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); -+ -+} -+ -+/* -+ * 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); -+} - --#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) -+#elif !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) - - /* - * Load an endpoint's FIFO -@@ -227,8 +263,7 @@ void musb_read_fifo(struct musb_hw_ep *h - readsb(fifo, dst, len); - } - } -- --#endif /* normal PIO */ -+#endif /* !T6010 && !BLACKFIN */ - - - /*-------------------------------------------------------------------------*/ -@@ -874,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); -@@ -1081,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. -@@ -1240,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 -@@ -1256,6 +1299,11 @@ 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); -@@ -1276,14 +1324,27 @@ 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; - } -@@ -1393,12 +1454,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); -@@ -1462,8 +1527,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; -@@ -1472,10 +1537,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); - -@@ -2210,6 +2282,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,12 @@ struct musb { - * queue until it completes or NAKs too much; then we try the next - * endpoint. - */ -+#ifdef CONFIG_UBICOM32 -+ struct musb_hw_ep *bulk_ep_in; -+ struct musb_hw_ep *bulk_ep_out; -+#else - struct musb_hw_ep *bulk_ep; -+#endif - - struct list_head control; /* of musb_qh */ - struct list_head in_bulk; /* of musb_qh */ ---- a/drivers/usb/musb/musb_gadget.c -+++ b/drivers/usb/musb/musb_gadget.c -@@ -432,7 +432,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) { -@@ -448,7 +448,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); -@@ -584,10 +584,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; -- unsigned fifo_count = 0; -- u16 len = musb_ep->packet_sz; -+ u16 fifo_count = 0; -+ 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); - -@@ -726,7 +732,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); - } -@@ -745,10 +751,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); -@@ -1769,7 +1780,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 -@@ -160,7 +160,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); -+#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; -@@ -223,6 +227,8 @@ musb_start_urb(struct musb *musb, int is - break; - default: /* bulk, interrupt */ - /* 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; - } -@@ -342,13 +348,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)); - } - } - -@@ -556,7 +562,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 { -@@ -590,6 +600,7 @@ 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); -@@ -604,7 +615,18 @@ musb_rx_reinit(struct musb *musb, struct - */ - if (csr & MUSB_TXCSR_DMAMODE) - musb_writew(ep->regs, MUSB_TXCSR, MUSB_TXCSR_DMAMODE); -+ -+#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 { -@@ -1138,8 +1160,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); - -@@ -1180,9 +1212,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 - return; - } - -@@ -1353,8 +1390,14 @@ void musb_host_tx(struct musb *musb, u8 - qh->segsize = length; - - 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_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); -+ MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY); -+#endif -+ - } - - -@@ -1414,7 +1457,11 @@ static void musb_bulk_rx_nak_timeout(str - - /* clear nak timeout bit */ - rx_csr = musb_readw(epio, MUSB_RXCSR); -+#ifndef CONFIG_UBICOM32 - rx_csr |= MUSB_RXCSR_H_WZC_BITS; -+#else -+ rx_csr &= ~MUSB_RXCSR_H_WZC_BITS; -+#endif - rx_csr &= ~MUSB_RXCSR_DATAERROR; - musb_writew(epio, MUSB_RXCSR, rx_csr); - -@@ -1483,6 +1530,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); -@@ -1521,8 +1575,15 @@ void musb_host_rx(struct musb *musb, u8 - return; - } - musb_ep_select(mbase, epnum); -+#ifndef CONFIG_UBICOM32 - rx_csr |= MUSB_RXCSR_H_WZC_BITS; - rx_csr &= ~MUSB_RXCSR_DATAERROR; -+#else -+ /* NEED TO EVALUATE CHANGE */ -+ rx_csr &= ~MUSB_RXCSR_H_WZC_BITS; -+ rx_csr &= ~MUSB_RXCSR_DATAERROR; -+// musb_writew(epio, MUSB_RXCSR, (~(MUSB_RXCSR_H_WZC_BITS))| MUSB_RXCSR_H_REQPKT); -+#endif - musb_writew(epio, MUSB_RXCSR, rx_csr); - - goto finish; -@@ -1579,8 +1640,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)) { -@@ -1610,7 +1676,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 -@@ -1823,7 +1889,11 @@ static int musb_schedule( - } else if (hw_ep->out_qh != NULL) - continue; - -+#ifndef CONFIG_UBICOM32 - if (hw_ep == musb->bulk_ep) -+#else -+ if ((hw_ep == musb->bulk_ep_in) || (hw_ep == musb->bulk_ep_out)) /* Ubicom */ -+#endif - continue; - - if (is_in) -@@ -1836,7 +1906,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) -@@ -1858,6 +1935,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; -@@ -1869,6 +1962,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) -@@ -1975,6 +2075,15 @@ static int musb_urb_enqueue( - /* ISO always uses logarithmic encoding */ - interval = min_t(u8, epd->bInterval, 16); - 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 -@@ -58,6 +58,7 @@ static inline void writesb(const void __ - - #ifndef CONFIG_BLACKFIN - -+#ifndef CONFIG_UBICOM32 - /* NOTE: these offsets are all in bytes */ - - static inline u16 musb_readw(const void __iomem *addr, unsigned offset) -@@ -72,7 +73,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 -+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 - -@@ -106,7 +137,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); } ---- a/drivers/usb/musb/musb_regs.h -+++ b/drivers/usb/musb/musb_regs.h -@@ -167,6 +167,7 @@ - (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ - | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) - -+ - /* RXCSR in Peripheral and Host mode */ - #define MUSB_RXCSR_AUTOCLEAR 0x8000 - #define MUSB_RXCSR_DMAENAB 0x2000 ---- /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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#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 -@@ -93,6 +93,63 @@ config LCD_HP700 - If you have an HP Jornada 700 series handheld (710/720/728) - say Y to enable LCD control driver. - -+config LCD_UBICOM32POWER -+ tristate "Ubicom LCD power Driver" -+ depends on LCD_CLASS_DEVICE && UBICOM32 -+ default n -+ help -+ If you have a Ubicom32 based system with an LCD panel that requires -+ power control, say Y to enable the power control driver for it. -+ -+config LCD_UBICOM32 -+ tristate "Ubicom Backlight Driver" -+ depends on LCD_CLASS_DEVICE && UBICOM32 -+ default n -+ help -+ This driver takes care of initialization of LCD panels with -+ built in controllers. -+ -+menu "Ubicom32 LCD Panel Support" -+ depends on UBICOM32 && LCD_UBICOM32 -+ -+config LCD_UBICOM32_TFT2N0369E_P -+ bool "TFT2N0369E (Portrait)" -+ default n -+ help -+ Support for TFT2N0369 in portrait mode -+ -+config LCD_UBICOM32_TFT2N0369E_L -+ bool "TFT2N0369E (Landscape)" -+ default n -+ help -+ Support for TFT2N0369 in landscape mode -+ -+config LCD_UBICOM32_CFAF240320KTTS -+ bool "CFAF240320KTTS" -+ default n -+ help -+ Support for CFAF240320KTTS -+ -+config LCD_UBICOM32_CFAF240320KTTS_180 -+ bool "CFAF240320KTTS (180 rotation)" -+ default n -+ help -+ Support for CFAF240320KTTS rotated 180 degrees -+ -+config LCD_UBICOM32_CFAF240320D -+ bool "CFAF240320D" -+ default n -+ help -+ Support for CFAF240320D -+ -+config LCD_UBICOM32_CFAF320240F -+ bool "CFAF320240F" -+ default n -+ help -+ Support for CFAF320240F -+ -+endmenu -+ - # - # Backlight - # -@@ -229,3 +286,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 -@@ -9,6 +9,9 @@ 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_UBICOM32POWER) += ubicom32lcdpower.o -+obj-$(CONFIG_LCD_UBICOM32) += ubicom32lcd.o - - obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o - obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o -@@ -24,4 +27,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,399 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#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 (PA5) -+ */ -+ UBICOM32_IO_PORT(RC)->ctl0 &= ~0xFFFF; -+ UBICOM32_IO_PORT(RC)->ctl0 |= pwm_cfg; -+ UBICOM32_IO_PORT(RC)->ctl1 = ud->pdata->pwm_period << 16; -+ -+ /* -+ * If the port function is not set, set it to GPIO/PWM -+ */ -+ if (!UBICOM32_IO_PORT(RA)->function) { -+ UBICOM32_IO_PORT(RA)->function = 3; -+ } -+ break; -+ -+ case 1: -+ /* -+ * Channel 1 is in the upper half of PORT C ctl0 and ctl2 (PE4) -+ */ -+ UBICOM32_IO_PORT(RC)->ctl0 &= ~0xFFFF0000; -+ UBICOM32_IO_PORT(RC)->ctl0 |= (pwm_cfg << 16); -+ UBICOM32_IO_PORT(RC)->ctl2 = ud->pdata->pwm_period << 16; -+ -+ /* -+ * If the port function is not set, set it to GPIO/ExtIOInt -+ */ -+ if (!UBICOM32_IO_PORT(RE)->function) { -+ UBICOM32_IO_PORT(RE)->function = 3; -+ } -+ break; -+ -+ case 2: -+ /* -+ * Channel 2 is in PORT H ctl0 and ctl1 (PD0) -+ */ -+ UBICOM32_IO_PORT(RH)->ctl0 &= ~0xFFFF0000; -+ UBICOM32_IO_PORT(RH)->ctl0 = pwm_cfg; -+ UBICOM32_IO_PORT(RH)->ctl1 = ud->pdata->pwm_period << 16; -+ -+ /* -+ * If the port function is not set, set it to GPIO -+ */ -+ if (!UBICOM32_IO_PORT(RD)->function) { -+ UBICOM32_IO_PORT(RD)->function = 3; -+ } -+ 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,372 @@ -+/* -+ * drivers/video/ubicom32lcd.c -+ * LCD initilization 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 . -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "ubicom32lcd.h" -+ -+#define DRIVER_NAME "ubicom32lcd" -+ -+struct ubicom32lcd_data { -+ const struct ubicom32lcd_panel *panel; -+ -+ int pin_cs; -+ int pin_rd; -+ int pin_rs; -+ int pin_wr; -+ int pin_reset; -+ struct ubicom32_io_port *port_data; -+ int data_shift; -+}; -+ -+/* -+ * ubicom32lcd_write -+ * Performs a write cycle on the bus (assumes CS asserted, RD & WR set) -+ */ -+static void ubicom32lcd_write(struct ubicom32lcd_data *ud, int command, u16 data) -+{ -+ if (command) { -+ UBICOM32_GPIO_SET_PIN_LOW(ud->pin_rs); -+ } else { -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); -+ } -+ -+ asm volatile ( -+ "or.4 4(%[port]), 4(%[port]), %[mask] \n\t" -+ "not.4 %[mask], %[mask] \n\t" -+ "and.4 8(%[port]), 8(%[port]), %[mask] \n\t" -+ "or.4 8(%[port]), 8(%[port]), %[cmd] \n\t" -+ : -+ : [port] "a" (ud->port_data), -+ [mask] "d" (0xFFFF << ud->data_shift), -+ [cmd] "d" (data << ud->data_shift) -+ : "cc" -+ ); -+ -+ UBICOM32_GPIO_SET_PIN_LOW(ud->pin_wr); -+ -+ //ndelay(50); -+ udelay(1); -+ -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); -+ -+ udelay(1); -+ //ndelay(50); -+} -+ -+/* -+ * ubicom32lcd_read_data -+ * Performs a read cycle on the bus (assumes CS asserted, RD & WR set) -+ */ -+static u16 ubicom32lcd_read_data(struct ubicom32lcd_data *ud) -+{ -+ u32_t data; -+ -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); -+ -+ asm volatile ( -+ "and.4 4(%[port]), 4(%[port]), %[mask]\n\t" -+ : -+ : [port] "a" (ud->port_data), -+ [mask] "d" (~(0xFFFF << ud->data_shift)) -+ : "cc" -+ ); -+ -+ UBICOM32_GPIO_SET_PIN_LOW(ud->pin_rd); -+ -+ ndelay(300); -+ -+ asm volatile ( -+ "lsr.4 %[data], 12(%[port]), %[shamt] \n\t" -+ "and.4 %[data], %[data], %[mask] \n\t" -+ : [data] "=d" (data) -+ : [port] "a" (ud->port_data), -+ [mask] "d" (0xFFFF), -+ [shamt] "d" (ud->data_shift) -+ : "cc" -+ ); -+ -+ ndelay(200); -+ -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); -+ -+ ndelay(500); -+ -+ return data; -+} -+ -+/* -+ * ubicom32lcd_execute -+ * Executes a script for performing operations on the LCD (assumes CS set) -+ */ -+static void ubicom32lcd_execute(struct ubicom32lcd_data *ud, const struct ubicom32lcd_step *script) -+{ -+ while (1) { -+ switch (script->op) { -+ case LCD_STEP_CMD: -+ ubicom32lcd_write(ud, 1, script->cmd); -+ break; -+ -+ case LCD_STEP_DATA: -+ ubicom32lcd_write(ud, 0, script->data); -+ break; -+ -+ case LCD_STEP_CMD_DATA: -+ ubicom32lcd_write(ud, 1, script->cmd); -+ ubicom32lcd_write(ud, 0, script->data); -+ break; -+ -+ case LCD_STEP_SLEEP: -+ udelay(script->data); -+ break; -+ -+ case LCD_STEP_DONE: -+ return; -+ } -+ script++; -+ } -+} -+ -+/* -+ * ubicom32lcd_goto -+ * Places the gram pointer at a specific X, Y address -+ */ -+static void ubicom32lcd_goto(struct ubicom32lcd_data *ud, int x, int y) -+{ -+ ubicom32lcd_write(ud, 1, ud->panel->horz_reg); -+ ubicom32lcd_write(ud, 0, x); -+ ubicom32lcd_write(ud, 1, ud->panel->vert_reg); -+ ubicom32lcd_write(ud, 0, y); -+ ubicom32lcd_write(ud, 1, ud->panel->gram_reg); -+} -+ -+/* -+ * ubicom32lcd_panel_init -+ * Initializes the lcd panel. -+ */ -+static int ubicom32lcd_panel_init(struct ubicom32lcd_data *ud) -+{ -+ u16 id; -+ -+ UBICOM32_GPIO_SET_PIN_LOW(ud->pin_reset); -+ UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_reset); -+ UBICOM32_GPIO_ENABLE(ud->pin_reset); -+ -+ asm volatile ( -+ "or.4 0x50(%[port]), 0x50(%[port]), %[mask] \n\t" -+ "not.4 %[mask], %[mask] \n\t" -+ "and.4 0x04(%[port]), 0x04(%[port]), %[mask] \n\t" -+ : -+ : [port] "a" (ud->port_data), -+ [mask] "d" (0xFFFF << ud->data_shift) -+ : "cc" -+ ); -+ -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); -+ -+ UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_rs); -+ UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_rd); -+ UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_wr); -+ UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_cs); -+ -+ UBICOM32_GPIO_ENABLE(ud->pin_rs); -+ UBICOM32_GPIO_ENABLE(ud->pin_rd); -+ UBICOM32_GPIO_ENABLE(ud->pin_wr); -+ UBICOM32_GPIO_ENABLE(ud->pin_cs); -+ -+ udelay(20); -+ -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_reset); -+ -+ udelay(20); -+ -+ UBICOM32_GPIO_SET_PIN_LOW(ud->pin_cs); -+ -+ id = ubicom32lcd_read_data(ud); -+ -+ /* -+ * We will try to figure out what kind of panel we have if we were not told. -+ */ -+ if (!ud->panel) { -+ const struct ubicom32lcd_panel **p = ubicom32lcd_panels; -+ while (*p) { -+ if ((*p)->id && ((*p)->id == id)) { -+ break; -+ } -+ p++; -+ } -+ if (!*p) { -+ printk(KERN_WARNING DRIVER_NAME ":Could not find compatible panel, id=%x\n", id); -+ return -ENODEV; -+ } -+ ud->panel = *p; -+ } -+ -+ /* -+ * Make sure panel ID matches if we were supplied a panel type -+ */ -+ if (ud->panel->id && (ud->panel->id != id)) { -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); -+ -+ return -ENODEV; -+ } -+ -+ ubicom32lcd_execute(ud, ud->panel->init_seq); -+ -+ ubicom32lcd_goto(ud, 0, 0); -+ -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs); -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd); -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr); -+ UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs); -+ -+ printk(KERN_INFO DRIVER_NAME ": Initialized panel %s\n", ud->panel->desc); -+ -+ return 0; -+} -+ -+/* -+ * ubicom32lcd_probe -+ */ -+static int ubicom32lcd_probe(struct platform_device *pdev) -+{ -+ const struct ubicom32lcd_platform_data *pdata = pdev->dev.platform_data; -+ struct ubicom32lcd_data *ud; -+ int retval; -+ -+ /* -+ * Allocate our private data -+ */ -+ ud = kzalloc(sizeof(struct ubicom32lcd_data), GFP_KERNEL); -+ if (!ud) { -+ return -ENOMEM; -+ } -+ -+ if (pdata) { -+ ud->pin_cs = pdata->pin_cs; -+ ud->pin_rd = pdata->pin_rd; -+ ud->pin_wr = pdata->pin_wr; -+ ud->pin_rs = pdata->pin_rs; -+ ud->pin_reset = pdata->pin_reset; -+ ud->port_data = pdata->port_data; -+ ud->data_shift = pdata->data_shift; -+ } else { -+ /* -+ * Defaults -+ */ -+ ud->pin_cs = GPIO_RD_4; -+ ud->pin_rd = GPIO_RD_5; -+ ud->pin_rs = GPIO_RD_3; -+ ud->pin_wr = GPIO_RD_2; -+ ud->pin_reset = GPIO_RD_7; -+ ud->port_data = (struct ubicom32_io_port *)RI; -+ ud->data_shift = 0; -+ } -+ -+ /* -+ * Initialize the display -+ */ -+ retval = ubicom32lcd_panel_init(ud); -+ if (retval) { -+ kfree(ud); -+ return retval; -+ } -+ -+ printk(KERN_INFO DRIVER_NAME ": LCD initialized\n"); -+ -+ return 0; -+} -+ -+/* -+ * ubicom32lcd_remove -+ */ -+static int __exit ubicom32lcd_remove(struct platform_device *pdev) -+{ -+ struct ubicom32lcd_data *ud = platform_get_drvdata(pdev); -+ -+ kfree(ud); -+ -+ return 0; -+} -+ -+static struct platform_driver ubicom32lcd_driver = { -+ .probe = ubicom32lcd_probe, -+ .remove = ubicom32lcd_remove, -+ -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+ -+ .remove = __exit_p(ubicom32lcd_remove), -+}; -+ -+static struct platform_device *ubicom32lcd_device; -+ -+/* -+ * ubicom32lcd_init -+ */ -+static int __init ubicom32lcd_init(void) -+{ -+ int res; -+ -+ res = platform_driver_register(&ubicom32lcd_driver); -+ if (res == 0) { -+ ubicom32lcd_device = platform_device_alloc(DRIVER_NAME, 0); -+ if (ubicom32lcd_device) { -+ res = platform_device_add(ubicom32lcd_device); -+ } else { -+ res = -ENOMEM; -+ } -+ if (res) { -+ platform_device_put(ubicom32lcd_device); -+ platform_driver_unregister(&ubicom32lcd_driver); -+ } -+ } -+ return res; -+} -+module_init(ubicom32lcd_init); -+ -+/* -+ * ubicom32lcd_exit -+ */ -+static void __exit ubicom32lcd_exit(void) -+{ -+ platform_device_unregister(ubicom32lcd_device); -+ platform_driver_unregister(&ubicom32lcd_driver); -+} -+module_exit(ubicom32lcd_exit); -+ -+MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); -+MODULE_DESCRIPTION("Ubicom32 LCD driver"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/video/backlight/ubicom32lcd.h -@@ -0,0 +1,546 @@ -+/* -+ * ubicom32lcd.h -+ * Ubicom32 lcd panel drivers -+ * -+ * (C) Copyright 2009, Ubicom, Inc. -+ * -+ * This file is part of the Ubicom32 Linux Kernel Port. -+ * -+ * This Ubicom32 library is free software: you can redistribute -+ * it and/or modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation, either version 2 of the -+ * License, or (at your option) any later version. -+ * -+ * This Ubicom32 library is distributed in the hope that it -+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied -+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . -+ */ -+ -+#ifndef _UBICOM32LCD_H_ -+#define _UBICOM32LCD_H_ -+ -+enum ubicom32lcd_op { -+ /* -+ * Sleep for (data) ms -+ */ -+ LCD_STEP_SLEEP, -+ -+ /* -+ * Execute write of command -+ */ -+ LCD_STEP_CMD, -+ -+ /* -+ * Execute write of data -+ */ -+ LCD_STEP_DATA, -+ -+ /* -+ * Execute write of command/data -+ */ -+ LCD_STEP_CMD_DATA, -+ -+ /* -+ * Script done -+ */ -+ LCD_STEP_DONE, -+}; -+ -+struct ubicom32lcd_step { -+ enum ubicom32lcd_op op; -+ u16 cmd; -+ u16 data; -+}; -+ -+struct ubicom32lcd_panel { -+ const struct ubicom32lcd_step *init_seq; -+ const char *desc; -+ -+ u32 xres; -+ u32 yres; -+ u32 stride; -+ u32 flags; -+ -+ u16 id; -+ u16 horz_reg; -+ u16 vert_reg; -+ u16 gram_reg; -+}; -+ -+#ifdef CONFIG_LCD_UBICOM32_CFAF240320KTTS -+static const struct ubicom32lcd_step cfaf240320ktts_init_0[] = { -+ {LCD_STEP_CMD_DATA, 0x0001, 0x0000,}, // Driver Output Control Register (R01h) Page 14, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0002, 0x0700,}, // LCD Driving Waveform Control (R02h) Page 15, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0003, 0x50A0,}, // Entry Mode (R03h) 0 degrees -+ {LCD_STEP_CMD_DATA, 0x0004, 0x0000,}, // Scaling Control register (R04h) Page 16, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0008, 0x0207,}, // Display Control 2 (R08h) Page 17, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0009, 0x0000,}, // Display Control 3 (R09h) Page 18, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x000A, 0x0000,}, // Frame Cycle Control (R0Ah) Page 19, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x000C, 0x0000,}, // External Display Interface Control 1 (R0Ch) Page 20, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x000D, 0x0000,}, // Frame Maker Position (R0Dh) Page 21, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x000F, 0x0000,}, // External Display Interface Control 2 (R0Fh) Page 21, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0010, 0x0000,}, // Power Control 1 (R10h) Page 22, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0011, 0x0007,}, // Power Control 2 (R11h) Page 23, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0012, 0x0000,}, // Power Control 3 (R12h) Page 24, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0013, 0x0000,}, // Power Control 4 (R13h) Page 25, SPFD5408B Datasheet -+ {LCD_STEP_SLEEP, 0, 200}, -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0101,}, // Display Control (R07h) Page 16, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0010, 0x12B0,}, // Power Control 1 (R10h) Page 22, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0011, 0x0007,}, // Power Control 2 (R11h) Page 23, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0012, 0x01BB,}, // Power Control 3 (R12h) Page 24, SPFD5408B Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0013, 0x1300,}, // Power Control 4 (R13h) Page 25, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0029, 0x0010,}, // NVM read data 2 (R29h) Page 30, SPFD5408B Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0030, 0x000A,}, // Gamma Control 1 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0031, 0x1326,}, // Gamma Control 2 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0032, 0x0A29,}, // Gamma Control 3 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0033, 0x290A,}, // Gamma Control 4 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0034, 0x2613,}, // Gamma Control 5 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0035, 0x0A0A,}, // Gamma Control 6 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0036, 0x1E03,}, // Gamma Control 7 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0037, 0x031E,}, // Gamma Control 8 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0038, 0x0706,}, // Gamma Control 9 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0039, 0x0303,}, // Gamma Control 10 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003A, 0x0E04,}, // Gamma Control 11 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003B, 0x0E01,}, // Gamma Control 12 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003C, 0x010E,}, // Gamma Control 13 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003D, 0x040E,}, // Gamma Control 14 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003E, 0x0303,}, // Gamma Control 15 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003F, 0x0607,}, // Gamma Control 16 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0050, 0x0000,}, // Window Horizontal RAM Address Start (R50h) Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0051, 0x00EF,}, // Window Horizontal RAM Address End (R51h) Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0052, 0x0000,}, // Window Vertical RAM Address Start (R52h) Page 33, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0053, 0x013F,}, // Window Vertical RAM Address End (R53h) Page 33, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0060, 0x2700,}, // Driver Output Control (R60h) Page 33, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0061, 0x0001,}, // Driver Output Control (R61h) Page 35, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x006A, 0x0000,}, // Vertical Scroll Control (R6Ah) Page 35, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0080, 0x0000,}, // Display Position - Partial Display 1 (R80h) Page 35, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0081, 0x0000,}, // RAM Address Start - Partial Display 1 (R81h) Page 35, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0082, 0x0000,}, // RAM Address End - Partial Display 1 (R82h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0083, 0x0000,}, // Display Position - Partial Display 2 (R83h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0084, 0x0000,}, // RAM Address Start - Partial Display 2 (R84h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0085, 0x0000,}, // RAM Address End - Partial Display 2 (R85h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0090, 0x0010,}, // Panel Interface Control 1 (R90h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0092, 0x0000,}, // Panel Interface Control 2 (R92h) Page 37, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0093, 0x0103,}, // Panel Interface control 3 (R93h) Page 38, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0095, 0x0210,}, // Panel Interface control 4 (R95h) Page 38, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0097, 0x0000,}, // Panel Interface Control 5 (R97h) Page 40, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0098, 0x0000,}, // Panel Interface Control 6 (R98h) Page 41, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0173,}, // Display Control (R07h) Page 16, SPFD5408B Datasheet -+ {LCD_STEP_DONE, 0, 0}, -+}; -+ -+const struct ubicom32lcd_panel cfaf240320ktts_0 = { -+ .desc = "CFAF240320KTTS", -+ .init_seq = cfaf240320ktts_init_0, -+ .horz_reg = 0x20, -+ .vert_reg = 0x21, -+ .gram_reg = 0x22, -+ .xres = 240, -+ .yres = 320, -+ .stride = 240, -+ .id = 0x5408, -+}; -+#endif -+ -+#ifdef CONFIG_LCD_UBICOM32_CFAF240320KTTS_180 -+static const struct ubicom32lcd_step cfaf240320ktts_init_180[] = { -+ {LCD_STEP_CMD_DATA, 0x0001, 0x0000,}, // Driver Output Control Register (R01h) Page 14, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0002, 0x0700,}, // LCD Driving Waveform Control (R02h) Page 15, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0003, 0x5000,}, // Entry Mode (R03h) 180 degrees -+ {LCD_STEP_CMD_DATA, 0x0004, 0x0000,}, // Scaling Control register (R04h) Page 16, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0008, 0x0207,}, // Display Control 2 (R08h) Page 17, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0009, 0x0000,}, // Display Control 3 (R09h) Page 18, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x000A, 0x0000,}, // Frame Cycle Control (R0Ah) Page 19, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x000C, 0x0000,}, // External Display Interface Control 1 (R0Ch) Page 20, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x000D, 0x0000,}, // Frame Maker Position (R0Dh) Page 21, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x000F, 0x0000,}, // External Display Interface Control 2 (R0Fh) Page 21, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0010, 0x0000,}, // Power Control 1 (R10h) Page 22, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0011, 0x0007,}, // Power Control 2 (R11h) Page 23, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0012, 0x0000,}, // Power Control 3 (R12h) Page 24, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0013, 0x0000,}, // Power Control 4 (R13h) Page 25, SPFD5408B Datasheet -+ {LCD_STEP_SLEEP, 0, 200}, -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0101,}, // Display Control (R07h) Page 16, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0010, 0x12B0,}, // Power Control 1 (R10h) Page 22, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0011, 0x0007,}, // Power Control 2 (R11h) Page 23, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0012, 0x01BB,}, // Power Control 3 (R12h) Page 24, SPFD5408B Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0013, 0x1300,}, // Power Control 4 (R13h) Page 25, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0029, 0x0010,}, // NVM read data 2 (R29h) Page 30, SPFD5408B Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0030, 0x000A,}, // Gamma Control 1 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0031, 0x1326,}, // Gamma Control 2 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0032, 0x0A29,}, // Gamma Control 3 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0033, 0x290A,}, // Gamma Control 4 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0034, 0x2613,}, // Gamma Control 5 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0035, 0x0A0A,}, // Gamma Control 6 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0036, 0x1E03,}, // Gamma Control 7 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0037, 0x031E,}, // Gamma Control 8 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0038, 0x0706,}, // Gamma Control 9 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0039, 0x0303,}, // Gamma Control 10 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003A, 0x0E04,}, // Gamma Control 11 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003B, 0x0E01,}, // Gamma Control 12 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003C, 0x010E,}, // Gamma Control 13 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003D, 0x040E,}, // Gamma Control 14 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003E, 0x0303,}, // Gamma Control 15 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x003F, 0x0607,}, // Gamma Control 16 Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0050, 0x0000,}, // Window Horizontal RAM Address Start (R50h) Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0051, 0x00EF,}, // Window Horizontal RAM Address End (R51h) Page 32, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0052, 0x0000,}, // Window Vertical RAM Address Start (R52h) Page 33, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0053, 0x013F,}, // Window Vertical RAM Address End (R53h) Page 33, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0060, 0x2700,}, // Driver Output Control (R60h) Page 33, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0061, 0x0001,}, // Driver Output Control (R61h) Page 35, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x006A, 0x0000,}, // Vertical Scroll Control (R6Ah) Page 35, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0080, 0x0000,}, // Display Position - Partial Display 1 (R80h) Page 35, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0081, 0x0000,}, // RAM Address Start - Partial Display 1 (R81h) Page 35, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0082, 0x0000,}, // RAM Address End - Partial Display 1 (R82h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0083, 0x0000,}, // Display Position - Partial Display 2 (R83h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0084, 0x0000,}, // RAM Address Start - Partial Display 2 (R84h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0085, 0x0000,}, // RAM Address End - Partial Display 2 (R85h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0090, 0x0010,}, // Panel Interface Control 1 (R90h) Page 36, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0092, 0x0000,}, // Panel Interface Control 2 (R92h) Page 37, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0093, 0x0103,}, // Panel Interface control 3 (R93h) Page 38, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0095, 0x0210,}, // Panel Interface control 4 (R95h) Page 38, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0097, 0x0000,}, // Panel Interface Control 5 (R97h) Page 40, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0098, 0x0000,}, // Panel Interface Control 6 (R98h) Page 41, SPFD5408B Datasheet -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0173,}, // Display Control (R07h) Page 16, SPFD5408B Datasheet -+ {LCD_STEP_DONE, 0, 0}, -+}; -+ -+const struct ubicom32lcd_panel cfaf240320ktts_180 = { -+ .desc = "CFAF240320KTTS 180", -+ .init_seq = cfaf240320ktts_init_180, -+ .horz_reg = 0x20, -+ .vert_reg = 0x21, -+ .gram_reg = 0x22, -+ .xres = 240, -+ .yres = 320, -+ .stride = 240, -+ .id = 0x5408, -+}; -+#endif -+ -+#ifdef CONFIG_LCD_UBICOM32_TFT2N0369E_P -+static const struct ubicom32lcd_step tft2n0369ep_init[] = { -+ {LCD_STEP_CMD_DATA, 0x0028, 0x0006}, -+ {LCD_STEP_CMD_DATA, 0x0000, 0x0001}, -+ {LCD_STEP_SLEEP, 0, 15}, -+ {LCD_STEP_CMD_DATA, 0x002B, 0x9532}, -+ {LCD_STEP_CMD_DATA, 0x0003, 0xAAAC}, -+ {LCD_STEP_CMD_DATA, 0x000C, 0x0002}, -+ {LCD_STEP_CMD_DATA, 0x000D, 0x000A}, -+ {LCD_STEP_CMD_DATA, 0x000E, 0x2C00}, -+ {LCD_STEP_CMD_DATA, 0x001E, 0x00AA}, -+ {LCD_STEP_CMD_DATA, 0x0025, 0x8000}, -+ {LCD_STEP_SLEEP, 0, 15}, -+ {LCD_STEP_CMD_DATA, 0x0001, 0x2B3F}, -+ {LCD_STEP_CMD_DATA, 0x0002, 0x0600}, -+ {LCD_STEP_CMD_DATA, 0x0010, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0011, 0x6030}, -+ {LCD_STEP_SLEEP, 0, 20}, -+ {LCD_STEP_CMD_DATA, 0x0005, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0006, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0016, 0xEF1C}, -+ {LCD_STEP_CMD_DATA, 0x0017, 0x0003}, -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0233}, -+ {LCD_STEP_CMD_DATA, 0x000B, 0x5312}, -+ {LCD_STEP_CMD_DATA, 0x000F, 0x0000}, -+ {LCD_STEP_SLEEP, 0, 20}, -+ {LCD_STEP_CMD_DATA, 0x0041, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0042, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0048, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0049, 0x013F}, -+ {LCD_STEP_CMD_DATA, 0x0044, 0xEF00}, -+ {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0046, 0x013F}, -+ {LCD_STEP_CMD_DATA, 0x004A, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x004B, 0x0000}, -+ {LCD_STEP_SLEEP, 0, 20}, -+ {LCD_STEP_CMD_DATA, 0x0030, 0x0707}, -+ {LCD_STEP_CMD_DATA, 0x0031, 0x0704}, -+ {LCD_STEP_CMD_DATA, 0x0032, 0x0204}, -+ {LCD_STEP_CMD_DATA, 0x0033, 0x0201}, -+ {LCD_STEP_CMD_DATA, 0x0034, 0x0203}, -+ {LCD_STEP_CMD_DATA, 0x0035, 0x0204}, -+ {LCD_STEP_CMD_DATA, 0x0036, 0x0204}, -+ {LCD_STEP_CMD_DATA, 0x0037, 0x0502}, -+ {LCD_STEP_CMD_DATA, 0x003A, 0x0302}, -+ {LCD_STEP_CMD_DATA, 0x003B, 0x0500}, -+ {LCD_STEP_SLEEP, 0, 20}, -+ {LCD_STEP_CMD_DATA, 0x0044, 239 << 8 | 0}, -+ {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0046, 319}, -+ {LCD_STEP_DONE, 0, 0}, -+}; -+ -+const struct ubicom32lcd_panel tft2n0369ep = { -+ .desc = "TFT2N0369E-Portrait", -+ .init_seq = tft2n0369ep_init, -+ .horz_reg = 0x4e, -+ .vert_reg = 0x4f, -+ .gram_reg = 0x22, -+ .xres = 240, -+ .yres = 320, -+ .stride = 240, -+ .id = 0x8989, -+}; -+#endif -+ -+#ifdef CONFIG_LCD_UBICOM32_TFT2N0369E_L -+static const struct ubicom32lcd_step tft2n0369e_init[] = { -+ {LCD_STEP_CMD_DATA, 0x0028, 0x0006}, -+ {LCD_STEP_CMD_DATA, 0x0000, 0x0001}, -+ {LCD_STEP_SLEEP, 0, 15}, -+ {LCD_STEP_CMD_DATA, 0x002B, 0x9532}, -+ {LCD_STEP_CMD_DATA, 0x0003, 0xAAAC}, -+ {LCD_STEP_CMD_DATA, 0x000C, 0x0002}, -+ {LCD_STEP_CMD_DATA, 0x000D, 0x000A}, -+ {LCD_STEP_CMD_DATA, 0x000E, 0x2C00}, -+ {LCD_STEP_CMD_DATA, 0x001E, 0x00AA}, -+ {LCD_STEP_CMD_DATA, 0x0025, 0x8000}, -+ {LCD_STEP_SLEEP, 0, 15}, -+ {LCD_STEP_CMD_DATA, 0x0001, 0x2B3F}, -+ {LCD_STEP_CMD_DATA, 0x0002, 0x0600}, -+ {LCD_STEP_CMD_DATA, 0x0010, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0011, 0x60A8}, -+ {LCD_STEP_SLEEP, 0, 20}, -+ {LCD_STEP_CMD_DATA, 0x0005, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0006, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0016, 0xEF1C}, -+ {LCD_STEP_CMD_DATA, 0x0017, 0x0003}, -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0233}, -+ {LCD_STEP_CMD_DATA, 0x000B, 0x5312}, -+ {LCD_STEP_CMD_DATA, 0x000F, 0x0000}, -+ {LCD_STEP_SLEEP, 0, 20}, -+ {LCD_STEP_CMD_DATA, 0x0041, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0042, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0048, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0049, 0x013F}, -+ {LCD_STEP_CMD_DATA, 0x0044, 0xEF00}, -+ {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0046, 0x013F}, -+ {LCD_STEP_CMD_DATA, 0x004A, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x004B, 0x0000}, -+ {LCD_STEP_SLEEP, 0, 20}, -+ {LCD_STEP_CMD_DATA, 0x0030, 0x0707}, -+ {LCD_STEP_CMD_DATA, 0x0031, 0x0704}, -+ {LCD_STEP_CMD_DATA, 0x0032, 0x0204}, -+ {LCD_STEP_CMD_DATA, 0x0033, 0x0201}, -+ {LCD_STEP_CMD_DATA, 0x0034, 0x0203}, -+ {LCD_STEP_CMD_DATA, 0x0035, 0x0204}, -+ {LCD_STEP_CMD_DATA, 0x0036, 0x0204}, -+ {LCD_STEP_CMD_DATA, 0x0037, 0x0502}, -+ {LCD_STEP_CMD_DATA, 0x003A, 0x0302}, -+ {LCD_STEP_CMD_DATA, 0x003B, 0x0500}, -+ {LCD_STEP_SLEEP, 0, 20}, -+ {LCD_STEP_CMD_DATA, 0x0044, 239 << 8 | 0}, -+ {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0046, 319}, -+ {LCD_STEP_DONE, 0, 0}, -+}; -+ -+const struct ubicom32lcd_panel tft2n0369e = { -+ .desc = "TFT2N0369E-Landscape", -+ .init_seq = tft2n0369e_init, -+ .horz_reg = 0x4e, -+ .vert_reg = 0x4f, -+ .gram_reg = 0x22, -+ .xres = 320, -+ .yres = 240, -+ .stride = 320, -+ .id = 0x8989, -+}; -+#endif -+ -+#ifdef CONFIG_LCD_UBICOM32_CFAF240400D -+static const struct ubicom32lcd_step cfaf240400d_init[] = { -+ {LCD_STEP_CMD_DATA, 0x0606, 0x0000}, // Pin Control (R606h) // Page 41 of SPFD5420A Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0001}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0110, 0x0001}, // Power Control 6(R110h) // Page 30 of SPFD5420A Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0100, 0x17B0}, // Power Control 1 (R100h) // Page 26 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0101, 0x0147}, // Power Control 2 (R101h) // Page 27 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0102, 0x019D}, // Power Control 3 (R102h) // Page 28 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0103, 0x3600}, // Power Control 4 (R103h) // Page 29 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0281, 0x0010}, // NVM read data 2 (R281h) // Page 34 of SPFD5420A Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0102, 0x01BD}, // Power Control 3 (R102h) // Page 28 of SPFD5420A Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ -+ //--------------- Power control 1~6 ---------------// -+ {LCD_STEP_CMD_DATA, 0x0100, 0x16B0}, // Power Control 1 (R100h) // Page 26 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0101, 0x0147}, // Power Control 2 (R101h) // Page 27 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0102, 0x01BD}, // Power Control 3 (R102h) // Page 28 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0103, 0x2d00}, // Power Control 4 (R103h) // Page 29 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0107, 0x0000}, // Power Control 5 (R107h) // Page 30 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0110, 0x0001}, // Power Control 6(R110h) // Page 30 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0280, 0x0000}, // NVM read data 1 (R280h) // Page 33 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0281, 0x0006}, // NVM read data 2 (R281h) // Page 34 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0282, 0x0000}, // NVM read data 3 (R282h) // Page 34 of SPFD5420A Datasheet -+ -+ //------- Gamma 2.2 control (R300h to R30Fh) ------// -+ {LCD_STEP_CMD_DATA, 0x0300, 0x0101}, -+ {LCD_STEP_CMD_DATA, 0x0301, 0x0b27}, -+ {LCD_STEP_CMD_DATA, 0x0302, 0x132a}, -+ {LCD_STEP_CMD_DATA, 0x0303, 0x2a13}, -+ {LCD_STEP_CMD_DATA, 0x0304, 0x270b}, -+ {LCD_STEP_CMD_DATA, 0x0305, 0x0101}, -+ {LCD_STEP_CMD_DATA, 0x0306, 0x1205}, -+ {LCD_STEP_CMD_DATA, 0x0307, 0x0512}, -+ {LCD_STEP_CMD_DATA, 0x0308, 0x0005}, -+ {LCD_STEP_CMD_DATA, 0x0309, 0x0003}, -+ {LCD_STEP_CMD_DATA, 0x030A, 0x0f04}, -+ {LCD_STEP_CMD_DATA, 0x030B, 0x0f00}, -+ {LCD_STEP_CMD_DATA, 0x030C, 0x000f}, -+ {LCD_STEP_CMD_DATA, 0x030D, 0x040f}, -+ {LCD_STEP_CMD_DATA, 0x030E, 0x0300}, -+ {LCD_STEP_CMD_DATA, 0x030F, 0x0500}, -+ -+ {LCD_STEP_CMD_DATA, 0x0400, 0x3500}, // Base Image Number of Line (R400h) // Page 36 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0401, 0x0001}, // Base Image Display Control (R401h) // Page 39 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0404, 0x0000}, // Based Image Vertical Scroll Control (R404h) // Page 40 of SPFD5420A Datasheet -+ -+ //--------------- Normal set ---------------// -+ {LCD_STEP_CMD_DATA, 0x0000, 0x0000}, // ID Read Register (R000h) // Page 13 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0001, 0x0100}, // Driver Output Control Register (R001h) // Page 14 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0002, 0x0100}, // LCD Driving Waveform Control (R002h) // Page 14 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0003, 0x1030}, // Entry Mode (R003h) // Page 15 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0006, 0x0000}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0008, 0x0808}, // Display Control 2 (R008h) // Page 17 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0009, 0x0001}, // Display Control 3 (R009h) // Page 18 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x000B, 0x0010}, // Low Power Control (R00Bh) // Page 19 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x000C, 0x0000}, // External Display Interface Control 1 (R00Ch) // Page 19 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x000F, 0x0000}, // External Display Interface Control 2 (R00Fh) // Page 20 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0001}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet -+ -+ //--------------- Panel interface control 1~6 ---------------// -+ {LCD_STEP_CMD_DATA, 0x0010, 0x0012}, // Panel Interface Control 1 (R010h) // Page 20 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0011, 0x0202}, // Panel Interface Control 2 (R011h) // Page 21 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0012, 0x0300}, // Panel Interface control 3 (R012h) // Page 22 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0020, 0x021E}, // Panel Interface control 4 (R020h) // Page 22 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0021, 0x0202}, // Panel Interface Control 5 (021Rh) // Page 24 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0022, 0x0100}, // Panel Interface Control 6 (R022h) // Page 25 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0090, 0x8000}, // Frame Marker Control (R090h) // Page 25 of SPFD5420A Datasheet -+ -+ //--------------- Partial display ---------------// -+ {LCD_STEP_CMD_DATA, 0x0210, 0x0000}, // Window Horizontal RAM Address Start (R210h) // Page 35 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0211, 0x00EF}, // Window Horziontal RAM Address End (R211h) // Page 35 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0212, 0x0000}, // Window Vertical RAM Address Start (R212h) // Page 35 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0213, 0x018F}, // Window Vertical RAM Address End (R213h) // Page 35 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0500, 0x0000}, // Display Position - Partial Display 1 (R500h) // Page 40 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0501, 0x0000}, // RAM Address Start - Partial Display 1 (R501h)// Page 40 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0502, 0x0000}, // RAM Address End - Partail Display 1 (R502h) // Page 40 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0503, 0x0000}, // Display Position - Partial Display 2 (R503h) // Page 40 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0504, 0x0000}, // RAM Address Start . Partial Display 2 (R504h)// Page 41 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0505, 0x0000}, // RAM Address End . Partial Display 2 (R505h) // Page 41 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0606, 0x0000}, // Pin Control (R606h) // Page 41 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x06F0, 0x0000}, // NVM Access Control (R6F0h) // Page 41 of SPFD5420A Datasheet -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0173}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet -+ {LCD_STEP_SLEEP, 0, 50}, -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0171}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet -+ {LCD_STEP_SLEEP, 0, 10}, -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0173}, // Display Control 1 (R007h) // Page 16 of SPFD5420A Datasheet -+ {LCD_STEP_DONE, 0, 0}, -+}; -+ -+const struct ubicom32lcd_panel cfaf240400d = { -+ .desc = "CFAF240400D", -+ .init_seq = cfaf240400d_init, -+ .horz_reg = 0x0200, -+ .vert_reg = 0x0201, -+ .gram_reg = 0x0202, -+ .xres = 240, -+ .yres = 400, -+ .stride = 240, -+ .id = 0x5420, -+}; -+#endif -+ -+#ifdef CONFIG_LCD_UBICOM32_CFAF240400F -+static const struct ubicom32lcd_step cfaf320240f_init[] = { -+ {LCD_STEP_CMD_DATA, 0x0028, 0x0006}, // VCOM OTP Page 55-56 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0000, 0x0001}, // start Oscillator Page 36 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0010, 0x0000}, // Sleep mode Page 49 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0001, 0x32EF}, // Driver Output Control Page 36-39 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0002, 0x0600}, // LCD Driving Waveform Control Page 40-42 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0003, 0x6A38}, // Power Control 1 Page 43-44 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0011, 0x6870}, // Entry Mode Page 50-52 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0X000F, 0x0000}, // Gate Scan Position Page 49 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0X000B, 0x5308}, // Frame Cycle Control Page 45 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x000C, 0x0003}, // Power Control 2 Page 47 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x000D, 0x000A}, // Power Control 3 Page 48 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x000E, 0x2E00}, // Power Control 4 Page 48 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x001E, 0x00BE}, // Power Control 5 Page 53 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0025, 0x8000}, // Frame Frequency Control Page 53 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0026, 0x7800}, // Analog setting Page 54 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x004E, 0x0000}, // Ram Address Set Page 58 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x004F, 0x0000}, // Ram Address Set Page 58 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0012, 0x08D9}, // Sleep mode Page 49 of SSD2119 datasheet -+ -+ // Gamma Control (R30h to R3Bh) -- Page 56 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0030, 0x0000}, -+ {LCD_STEP_CMD_DATA, 0x0031, 0x0104}, -+ {LCD_STEP_CMD_DATA, 0x0032, 0x0100}, -+ {LCD_STEP_CMD_DATA, 0x0033, 0x0305}, -+ {LCD_STEP_CMD_DATA, 0x0034, 0x0505}, -+ {LCD_STEP_CMD_DATA, 0x0035, 0x0305}, -+ {LCD_STEP_CMD_DATA, 0x0036, 0x0707}, -+ {LCD_STEP_CMD_DATA, 0x0037, 0x0300}, -+ {LCD_STEP_CMD_DATA, 0x003A, 0x1200}, -+ {LCD_STEP_CMD_DATA, 0x003B, 0x0800}, -+ -+ {LCD_STEP_CMD_DATA, 0x0007, 0x0033}, // Display Control Page 45 of SSD2119 datasheet -+ -+ {LCD_STEP_CMD_DATA, 0x0044, 0xEF00}, // Vertical RAM address position Page 57 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0045, 0x0000}, // Horizontal RAM address position Page 57 of SSD2119 datasheet -+ {LCD_STEP_CMD_DATA, 0x0046, 0x013F}, // Horizontal RAM address position Page 57 of SSD2119 datasheet -+ -+ {LCD_STEP_SLEEP, 0, 150}, -+ -+ {LCD_STEP_DONE, 0, 0}, -+}; -+ -+const struct ubicom32lcd_panel cfaf320240f = { -+ .desc = "CFAF320240F", -+ .init_seq = cfaf320240f_init, -+ .horz_reg = 0x4e, -+ .vert_reg = 0x4f, -+ .gram_reg = 0x22, -+ .xres = 320, -+ .yres = 240, -+ .stride = 320, -+ .id = 0x9919, -+}; -+#endif -+ -+const struct ubicom32lcd_panel *ubicom32lcd_panels[] = { -+#ifdef CONFIG_LCD_UBICOM32_CFAF240400KTTS_180 -+ &cfaf240320ktts_180, -+#endif -+#ifdef CONFIG_LCD_UBICOM32_CFAF240400KTTS -+ &cfaf240320ktts_0, -+#endif -+#ifdef CONFIG_LCD_UBICOM32_CFAF240400D -+ &cfaf240400d, -+#endif -+#ifdef CONFIG_LCD_UBICOM32_TFT2N0369E_P -+ &tft2n0369ep, -+#endif -+#ifdef CONFIG_LCD_UBICOM32_TFT2N0369E_L -+ &tft2n0369e, -+#endif -+#ifdef CONFIG_LCD_UBICOM32_CFAF240400F -+ &cfaf320240f, -+#endif -+ NULL, -+}; -+ -+#endif ---- /dev/null -+++ b/drivers/video/backlight/ubicom32lcdpower.c -@@ -0,0 +1,194 @@ -+/* -+ * drivers/video/backlight/ubicom32lcdpowerpower.c -+ * LCD power 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define DRIVER_NAME "ubicom32lcdpower" -+ -+struct ubicom32lcdpower_data { -+ /* -+ * Pointer to the platform data structure. Keep this around since we need values -+ * from it to set the backlight intensity. -+ */ -+ const struct ubicom32lcdpower_platform_data *pdata; -+ -+ /* -+ * LCD device, we have to save this for use when we remove ourselves. -+ */ -+ struct lcd_device *lcddev; -+}; -+ -+/* -+ * ubicom32lcdpower_set_power -+ */ -+static int ubicom32lcdpower_set_power(struct lcd_device *ld, int power) -+{ -+ struct ubicom32lcdpower_data *ud = (struct ubicom32lcdpower_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; -+} -+ -+/* -+ * ubicom32lcdpower_get_power -+ */ -+static int ubicom32lcdpower_get_power(struct lcd_device *ld) -+{ -+ struct ubicom32lcdpower_data *ud = (struct ubicom32lcdpower_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 ubicom32lcdpower_ops = { -+ .get_power = ubicom32lcdpower_get_power, -+ .set_power = ubicom32lcdpower_set_power, -+}; -+ -+/* -+ * ubicom32lcdpower_probe -+ */ -+static int ubicom32lcdpower_probe(struct platform_device *pdev) -+{ -+ const struct ubicom32lcdpower_platform_data *pdata = pdev->dev.platform_data; -+ struct ubicom32lcdpower_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 ubicom32lcdpower_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, &ubicom32lcdpower_ops); -+ if (IS_ERR(lcddev)) { -+ retval = PTR_ERR(lcddev); -+ goto fail; -+ } -+ -+ ud->lcddev = lcddev; -+ platform_set_drvdata(pdev, ud); -+ -+ ubicom32lcdpower_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; -+} -+ -+/* -+ * ubicom32lcdpower_remove -+ */ -+static int __exit ubicom32lcdpower_remove(struct platform_device *pdev) -+{ -+ struct ubicom32lcdpower_data *ud = platform_get_drvdata(pdev); -+ -+ lcd_device_unregister(ud->lcddev); -+ platform_set_drvdata(pdev, NULL); -+ kfree(ud); -+ -+ return 0; -+} -+ -+static struct platform_driver ubicom32lcdpower_driver = { -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+ -+ .remove = __exit_p(ubicom32lcdpower_remove), -+}; -+ -+/* -+ * ubicom32lcdpower_init -+ */ -+static int __init ubicom32lcdpower_init(void) -+{ -+ return platform_driver_probe(&ubicom32lcdpower_driver, ubicom32lcdpower_probe); -+} -+module_init(ubicom32lcdpower_init); -+ -+/* -+ * ubicom32lcdpower_exit -+ */ -+static void __exit ubicom32lcdpower_exit(void) -+{ -+ platform_driver_unregister(&ubicom32lcdpower_driver); -+} -+module_exit(ubicom32lcdpower_exit); -+ -+MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); -+MODULE_DESCRIPTION("Ubicom32 lcd power driver"); -+MODULE_LICENSE("GPL"); ---- a/drivers/video/Kconfig -+++ b/drivers/video/Kconfig -@@ -609,6 +609,50 @@ 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. -+ You can configure video memory by using kernel command line parameters, for example: -+ video=ubicomfb:vram_size=512,init_value=0xffff -+ -+config FB_UBICOM32_PLIO80 -+ tristate "Ubicom32 80 Bus PLIO 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 -+ select UBICOM32_PLIO -+ help -+ This is a framebuffer device driver for the Ubicom32 architecture. -+ You can configure the xres, yres and vram size (in kilobytes) by using -+ kernel command line parameters, for example: -+ video=ubicom32vfb:xres=320,yres=240,vram_size=512 -+ -+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. -+ You can configure the xres, yres and vram size (in kilobytes) by using -+ kernel command line parameters, for example: -+ video=ubicom32vfb:xres=320,yres=240,vram_size=512 -+ -+config FB_UBICOM32_VIRTUAL_NOAUTO -+ bool "Do not automatically load" -+ depends on FB_UBICOM32_VIRTUAL -+ help -+ Select this option to prevent the VFB from automatically loading at boot. - - config FB_STI - tristate "HP STI frame buffer device support" ---- a/drivers/video/Makefile -+++ b/drivers/video/Makefile -@@ -136,6 +136,10 @@ obj-$(CONFIG_FB_BF54X_LQ043) += bf54x- - obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o - obj-$(CONFIG_FB_MX3) += mx3fb.o - -+obj-$(CONFIG_FB_UBICOM32) += ubicom32fb.o -+obj-$(CONFIG_FB_UBICOM32_PLIO80) += ubicom32plio80.o -+obj-$(CONFIG_FB_UBICOM32_VIRTUAL) += ubicom32vfb.o -+ - # the test framebuffer is last - obj-$(CONFIG_FB_VIRTUAL) += vfb.o - ---- /dev/null -+++ b/drivers/video/ubicom32fb.c -@@ -0,0 +1,779 @@ -+/* -+ * 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 . -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#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_UBICOM32, -+}; -+ -+/* -+ * 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 = vram_size * 1024; -+ ud->fbinfo->fix.line_length = regs->xres * (regs->bpp / 8); -+ ud->fbinfo->fix.mmio_start = (u32)regs; -+ ud->fbinfo->fix.mmio_len = sizeof(struct vdc_tio_vp_regs); -+ -+ /* -+ * 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:') -+ */ -+ 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/ubicom32plio80.c -@@ -0,0 +1,780 @@ -+/* -+ * drivers/video/ubicom32plio80.c -+ * Ubicom32 80 bus PLIO 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 . -+ */ -+ -+/* -+ * This driver was based on skeletonfb.c, Skeleton for a frame buffer device by -+ * Geert Uytterhoeven. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DRIVER_NAME "ubicom32plio80" -+#define DRIVER_DESCRIPTION "Ubicom32 80 bus PLIO 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 = 240; -+module_param(xres, int, 0); -+MODULE_PARM_DESC(xres, "x (horizontal) resolution"); -+ -+static int yres = 320; -+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)"); -+ -+#define BITS_PER_PIXEL 16 -+ -+/* -+ * Buffer alignment, must not be 0 -+ */ -+#define UBICOM32PLIO80_ALIGNMENT 4 -+ -+/* -+ * PLIO FSM -+ * 16-bit data bus on port I -+ * CS on EXTCTL[6] -+ * WR on EXTCTL[4] -+ */ -+static const plio_fctl_t plio_fctl = { -+ .fctl0 = { -+ .ptif_port_mode = PLIO_PORT_MODE_DI, -+ .ptif_portd_cfg = 0, -+ .ptif_porti_cfg = 3, -+ .edif_ds = 6, -+ .edif_cmp_mode = 1, -+ .ecif_extclk_ena = 0, // enable clock output on PD7 table 2.65/p111 says extctl[0]? -+ .icif_clk_src_sel = PLIO_CLK_IO, -+ }, -+ .fctl2 = { -+ .icif_eclk_div = 10, -+ .icif_iclk_div = 10, -+ }, -+ -+ }; -+ -+ static const plio_config_t plio_config = { -+ .pfsm = { -+ /* -+ * Table 12.63 -+ */ -+ .grpsel[0] = {1,1,1,1,1,1,1,1,1,1}, -+ -+ /* -+ * Table 12.66 Counter load value -+ */ -+ .cs_lut[0] = {0,0,0,0,0,0,0,0}, -+ -+ /* -+ * Table 2.75 PLIO PFSM Configuration Registers -+ */ -+ // 3 2 1 0 -+ .extctl_o_lut[0] = {0x3f, 0x2f, 0x3f, 0x3f}, -+ // 7 6 5 4 -+ .extctl_o_lut[1] = {0x3f, 0x3f, 0x3f, 0x2f}, -+ }, -+ .edif = { -+ .odr_oe = 0xffff, -+ }, -+ .ecif = { -+ .output_ena = (1 << 6) | (1 << 4), -+ }, -+}; -+ -+static const u32_t ubicom32plio80_plio_fsm[] = { -+ // 0-F -+ 0x00070007, 0x00070007, -+ 0x00070007, 0x00070007, -+ 0x00070007, 0x00070007, -+ 0x00070007, 0x00070007, -+ -+ 0x16260806, 0x16260806, -+ 0x16260806, 0x16260806, -+ 0x16260806, 0x16260806, -+ 0x16260806, 0x16260806, -+ -+ // 10 - 1f -+ 0x22061806, 0x22061806, -+ 0x22061806, 0x22061806, -+ 0x22061806, 0x22061806, -+ 0x22061806, 0x22061806, -+ -+ 0x22061806, 0x22061806, -+ 0x22061806, 0x22061806, -+ 0x22061806, 0x22061806, -+ 0x22061806, 0x22061806, -+ -+ // 20 - 2f -+ 0x00070806, 0x00070806, -+ 0x00070806, 0x00070806, -+ 0x00070806, 0x00070806, -+ 0x00070806, 0x00070806, -+ -+ 0x00070806, 0x00070806, -+ 0x00070806, 0x00070806, -+ 0x00070806, 0x00070806, -+ 0x00070806, 0x00070806, -+}; -+ -+/* -+ * fb_fix_screeninfo defines the non-changeable properties of the VDC, depending on what mode it is in. -+ */ -+static struct fb_fix_screeninfo ubicom32plio80_fix = { -+ .id = "Ubicom32", -+ .type = FB_TYPE_PACKED_PIXELS, -+ .visual = FB_VISUAL_TRUECOLOR, -+ .accel = FB_ACCEL_UBICOM32_PLIO80, -+}; -+ -+/* -+ * Filled in at probe time when we find out what the hardware supports -+ */ -+static struct fb_var_screeninfo ubicom32plio80_var; -+ -+/* -+ * Private data structure -+ */ -+struct ubicom32plio80_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]; -+ -+ int irq_req; -+ -+ /* -+ * Current pointer and bytes left to transfer with the PLIO -+ */ -+ void *xfer_ptr; -+ u32 bytes_to_xfer; -+ u32 busy; -+}; -+ -+static struct platform_device *ubicom32plio80_platform_device; -+ -+/* -+ * ubicom32plio80_isr -+ */ -+static int ubicom32plio80_isr(int irq, void *appdata) -+{ -+ struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)appdata; -+ -+ if (!ud->bytes_to_xfer) { -+ ubicom32_disable_interrupt(TX_FIFO_INT(PLIO_PORT)); -+ PLIO_NBR->intmask.txfifo_wm = 0; -+ ud->busy = 0; -+ return IRQ_HANDLED; -+ } -+ -+ asm volatile ( -+ ".rept 8 \n\t" -+ "move.4 (%[fifo]), (%[data])4++ \n\t" -+ ".endr \n\t" -+ : [data] "+a" (ud->xfer_ptr) -+ : [fifo] "a" (&PLIO_NBR->tx_lo) -+ ); -+ -+ ud->bytes_to_xfer -= 32; -+ -+ return IRQ_HANDLED; -+} -+ -+/* -+ * ubicom32plio80_update -+ */ -+static void ubicom32plio80_update(struct ubicom32plio80_drvdata *ud, u32 *fb) -+{ -+ struct ubicom32_io_port *ri = (struct ubicom32_io_port *)RI; -+ struct ubicom32_io_port *rd = (struct ubicom32_io_port *)RD; -+ -+ ud->xfer_ptr = fb; -+ ud->bytes_to_xfer = (xres * yres * 2) - 64; -+ ud->busy = 1; -+ -+ ri->gpio_mask = 0; -+ rd->gpio_mask &= ~((1 << 4) | (1 << 2)); -+ -+ *(u32 *)(&PLIO_NBR->intclr) = ~0; -+ PLIO_NBR->intmask.txfifo_wm = 1; -+ PLIO_NBR->fifo_wm.tx = 8; -+ ubicom32_enable_interrupt(TX_FIFO_INT(PLIO_PORT)); -+ -+ asm volatile ( -+ ".rept 16 \n\t" -+ "move.4 (%[fifo]), (%[data])4++ \n\t" -+ ".endr \n\t" -+ : [data] "+a" (ud->xfer_ptr) -+ : [fifo] "a" (&PLIO_NBR->tx_lo) -+ ); -+} -+ -+/* -+ * ubicom32plio80_pan_display -+ * Pans the display to a given location. Supports only y direction panning. -+ */ -+static int ubicom32plio80_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) -+{ -+ struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_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; -+} -+ -+/* -+ * ubicom32plio80_setcolreg -+ * Sets a color in our virtual palette -+ */ -+static int ubicom32plio80_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; -+} -+ -+/* -+ * ubicom32plio80_mmap -+ */ -+static int ubicom32plio80_mmap(struct fb_info *info, struct vm_area_struct *vma) -+{ -+ struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)info->par; -+ -+ vma->vm_start = (unsigned long)(ud->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; -+} -+ -+/* -+ * ubicom32plio80_check_var -+ * Check the var, tweak it but don't change operational parameters. -+ */ -+static int ubicom32plio80_check_var(struct fb_var_screeninfo *var, struct fb_info *info) -+{ -+ struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)info->par; -+ u32 line_size = var->xres * (BITS_PER_PIXEL / 8); -+ -+ /* -+ * See if we can handle this bpp -+ */ -+ if (var->bits_per_pixel > BITS_PER_PIXEL) { -+ return -EINVAL; -+ } -+ var->bits_per_pixel = BITS_PER_PIXEL; -+ -+ /* -+ * See if we have enough memory to handle this resolution -+ */ -+ if ((line_size * var->yres * BITS_PER_PIXEL / 8) > ud->total_vram_size) { -+ return -EINVAL; -+ } -+ -+ var->xres_virtual = var->xres; -+ var->yres_virtual = ud->total_vram_size / line_size; -+ -+ var->red.length = 5; -+ var->green.length = 6; -+ var->green.offset = 5; -+ var->blue.length = 5; -+ var->transp.offset = var->transp.length = 0; -+ -+ if (bgr) { -+ var->red.offset = 0; -+ var->blue.offset = 11; -+ } else { -+ var->red.offset = 11; -+ var->blue.offset = 0; -+ } -+ -+ var->nonstd = 0; -+ var->height = -1; -+ var->width = -1; -+ var->vmode = FB_VMODE_NONINTERLACED; -+ var->sync = 0; -+ -+ return 0; -+} -+ -+/* -+ * ubicom32plio80_set_par -+ * Set the video mode according to info->var -+ */ -+static int ubicom32plio80_set_par(struct fb_info *info) -+{ -+ /* -+ * Anything changed? -+ */ -+ if ((xres == info->var.xres) && (yres == info->var.yres)) { -+ return 0; -+ } -+ -+ /* -+ * Implement changes -+ */ -+ xres = info->var.xres; -+ yres = info->var.yres; -+ info->fix.visual = FB_VISUAL_TRUECOLOR; -+ info->fix.xpanstep = 0; -+ info->fix.ypanstep = 1; -+ info->fix.line_length = xres * (BITS_PER_PIXEL / 8); -+ -+ return 0; -+} -+ -+/* -+ * ubicom32plio80_ops -+ * List of supported operations -+ */ -+static struct fb_ops ubicom32plio80_ops = -+{ -+ .owner = THIS_MODULE, -+ .fb_pan_display = ubicom32plio80_pan_display, -+ .fb_setcolreg = ubicom32plio80_setcolreg, -+ .fb_mmap = ubicom32plio80_mmap, -+ .fb_check_var = ubicom32plio80_check_var, -+ .fb_set_par = ubicom32plio80_set_par, -+ .fb_fillrect = cfb_fillrect, -+ .fb_copyarea = cfb_copyarea, -+ .fb_imageblit = cfb_imageblit, -+}; -+ -+/* -+ * ubicom32plio80_release -+ */ -+static int ubicom32plio80_release(struct device *dev) -+{ -+ struct ubicom32plio80_drvdata *ud = dev_get_drvdata(dev); -+ -+ unregister_framebuffer(ud->fbinfo); -+ -+ if (ud->irq_req) { -+ free_irq(TX_FIFO_INT(PLIO_PORT), ud); -+ } -+ 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; -+} -+ -+/* -+ * ubicom32plio80_platform_probe -+ */ -+static int __init ubicom32plio80_platform_probe(struct platform_device *pdev) -+{ -+ struct ubicom32plio80_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 ubicom32plio80_drvdata), &pdev->dev); -+ if (!fbinfo) { -+ dev_err(dev, "Not enough memory to allocate instance.\n"); -+ return -ENOMEM; -+ } -+ -+ /* -+ * Fill in our private data. -+ */ -+ ud = (struct ubicom32plio80_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) + UBICOM32PLIO80_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 & (UBICOM32PLIO80_ALIGNMENT - 1); -+ if (!offset) { -+ ud->fb_aligned = ud->fb; -+ } else { -+ offset = UBICOM32PLIO80_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(&ubicom32plio80_var, 0, sizeof(ubicom32plio80_var)); -+ ubicom32plio80_var.bits_per_pixel = BITS_PER_PIXEL; -+ ubicom32plio80_var.red.length = 5; -+ ubicom32plio80_var.green.length = 6; -+ ubicom32plio80_var.green.offset = 5; -+ ubicom32plio80_var.blue.length = 5; -+ ubicom32plio80_var.activate = FB_ACTIVATE_NOW; -+ -+ if (bgr) { -+ ubicom32plio80_var.red.offset = 0; -+ ubicom32plio80_var.blue.offset = 11; -+ } else { -+ ubicom32plio80_var.red.offset = 11; -+ ubicom32plio80_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 = &ubicom32plio80_ops; -+ ud->fbinfo->fix = ubicom32plio80_fix; -+ ud->fbinfo->fix.smem_start = (u32)ud->fb_aligned; -+ ud->fbinfo->fix.smem_len = vram_size * 1024; -+ ud->fbinfo->fix.line_length = xres * 2; -+ ud->fbinfo->fix.mmio_start = (u32)ud; -+ ud->fbinfo->fix.mmio_len = sizeof(struct ubicom32plio80_drvdata); -+ -+ /* -+ * 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 = ubicom32plio80_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; -+ } -+ -+ /* -+ * request the PLIO IRQ -+ */ -+ rc = request_irq(TX_FIFO_INT(PLIO_PORT), ubicom32plio80_isr, IRQF_DISABLED, "ubicom32plio80", ud); -+ if (rc) { -+ dev_err(dev, "Could not request IRQ\n"); -+ goto fail; -+ } -+ ud->irq_req = 1; -+ -+ /* -+ * Clear any garbage out of the TX FIFOs (idif_txfifo_flush) -+ * -+ * cast through ubicom32_io_port to make sure the compiler does a word write -+ */ -+ ((struct ubicom32_io_port *)PLIO_NBR)->int_set = (1 << 18); -+ -+ /* -+ * Start up the state machine -+ */ -+ plio_init(&plio_fctl, &plio_config, (plio_sram_t *)ubicom32plio80_plio_fsm, sizeof(ubicom32plio80_plio_fsm)); -+ PLIO_NBR->fctl0.pfsm_cmd = 0; -+ -+ ubicom32plio80_update(ud, ud->fb_aligned); -+ -+ /* -+ * 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: -+ ubicom32plio80_release(dev); -+ return rc; -+} -+ -+/* -+ * ubicom32plio80_platform_remove -+ */ -+static int ubicom32plio80_platform_remove(struct platform_device *pdev) -+{ -+ dev_info(&(pdev->dev), "Ubicom32 FB Driver Remove\n"); -+ return ubicom32plio80_release(&pdev->dev); -+} -+ -+static struct platform_driver ubicom32plio80_platform_driver = { -+ .probe = ubicom32plio80_platform_probe, -+ .remove = ubicom32plio80_platform_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+#ifndef MODULE -+/* -+ * ubicom32plio80_setup -+ * Process kernel boot options -+ */ -+static int __init ubicom32plio80_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 */ -+ -+/* -+ * ubicom32plio80_init -+ */ -+static int __devinit ubicom32plio80_init(void) -+{ -+ int ret; -+ -+#ifndef MODULE -+ /* -+ * Get kernel boot options (in 'video=ubicom32plio80:') -+ */ -+ char *option = NULL; -+ -+ if (fb_get_options(DRIVER_NAME, &option)) { -+ return -ENODEV; -+ } -+ ubicom32plio80_setup(option); -+#endif /* MODULE */ -+ -+ ret = platform_driver_register(&ubicom32plio80_platform_driver); -+ -+ if (!ret) { -+ ubicom32plio80_platform_device = platform_device_alloc(DRIVER_NAME, 0); -+ -+ if (ubicom32plio80_platform_device) -+ ret = platform_device_add(ubicom32plio80_platform_device); -+ else -+ ret = -ENOMEM; -+ -+ if (ret) { -+ platform_device_put(ubicom32plio80_platform_device); -+ platform_driver_unregister(&ubicom32plio80_platform_driver); -+ } -+ } -+ -+ return ret; -+} -+module_init(ubicom32plio80_init); -+ -+/* -+ * ubicom32plio80_exit -+ */ -+static void __exit ubicom32plio80_exit(void) -+{ -+ platform_device_unregister(ubicom32plio80_platform_device); -+ platform_driver_unregister(&ubicom32plio80_platform_driver); -+} -+module_exit(ubicom32plio80_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Patrick Tjin <@ubicom.com>"); -+MODULE_DESCRIPTION(DRIVER_DESCRIPTION); ---- /dev/null -+++ b/drivers/video/ubicom32vfb.c -@@ -0,0 +1,603 @@ -+/* -+ * 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 . -+ */ -+ -+/* -+ * This driver was based on skeletonfb.c, Skeleton for a frame buffer device by -+ * Geert Uytterhoeven. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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)"); -+ -+#define BITS_PER_PIXEL 16 -+ -+/* -+ * 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_UBICOM32_VFB, -+}; -+ -+/* -+ * 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]; -+}; -+ -+static struct platform_device *ubicom32vfb_platform_device; -+ -+/* -+ * 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 *ud = (struct ubicom32vfb_drvdata *)info->par; -+ -+ vma->vm_start = (unsigned long)(ud->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; -+} -+ -+/* -+ * ubicom32vfb_check_var -+ * Check the var, tweak it but don't change operational parameters. -+ */ -+static int ubicom32vfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) -+{ -+ struct ubicom32vfb_drvdata *ud = (struct ubicom32vfb_drvdata *)info->par; -+ u32 line_size = var->xres * (BITS_PER_PIXEL / 8); -+ -+ /* -+ * See if we can handle this bpp -+ */ -+ if (var->bits_per_pixel > BITS_PER_PIXEL) { -+ return -EINVAL; -+ } -+ var->bits_per_pixel = BITS_PER_PIXEL; -+ -+ /* -+ * See if we have enough memory to handle this resolution -+ */ -+ if ((line_size * var->yres * BITS_PER_PIXEL / 8) > ud->total_vram_size) { -+ return -EINVAL; -+ } -+ -+ var->xres_virtual = var->xres; -+ var->yres_virtual = ud->total_vram_size / line_size; -+ -+ var->red.length = 5; -+ var->green.length = 6; -+ var->green.offset = 5; -+ var->blue.length = 5; -+ var->transp.offset = var->transp.length = 0; -+ -+ if (bgr) { -+ var->red.offset = 0; -+ var->blue.offset = 11; -+ } else { -+ var->red.offset = 11; -+ var->blue.offset = 0; -+ } -+ -+ var->nonstd = 0; -+ var->height = -1; -+ var->width = -1; -+ var->vmode = FB_VMODE_NONINTERLACED; -+ var->sync = 0; -+ -+ return 0; -+} -+ -+/* -+ * ubicom32vfb_set_par -+ * Set the video mode according to info->var -+ */ -+static int ubicom32vfb_set_par(struct fb_info *info) -+{ -+ /* -+ * Anything changed? -+ */ -+ if ((xres == info->var.xres) && (yres == info->var.yres)) { -+ return 0; -+ } -+ -+ /* -+ * Implement changes -+ */ -+ xres = info->var.xres; -+ yres = info->var.yres; -+ info->fix.visual = FB_VISUAL_TRUECOLOR; -+ info->fix.xpanstep = 0; -+ info->fix.ypanstep = 1; -+ info->fix.line_length = xres * (BITS_PER_PIXEL / 8); -+ -+ return 0; -+} -+ -+/* -+ * ubicom32vfb_ops -+ * List of supported operations -+ */ -+static struct fb_ops ubicom32vfb_ops = -+{ -+ .owner = THIS_MODULE, -+ .fb_pan_display = ubicom32vfb_pan_display, -+ .fb_setcolreg = ubicom32vfb_setcolreg, -+ .fb_mmap = ubicom32vfb_mmap, -+ .fb_check_var = ubicom32vfb_check_var, -+ .fb_set_par = ubicom32vfb_set_par, -+ .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 = BITS_PER_PIXEL; -+ 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 = vram_size * 1024; -+ ud->fbinfo->fix.line_length = xres * 2; -+ ud->fbinfo->fix.mmio_start = (u32)ud; -+ ud->fbinfo->fix.mmio_len = sizeof(struct ubicom32vfb_drvdata); -+ -+ /* -+ * 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) -+{ -+ int ret; -+ -+#ifndef MODULE -+ /* -+ * Get kernel boot options (in 'video=ubicom32vfb:') -+ */ -+ char *option = NULL; -+ -+ if (fb_get_options(DRIVER_NAME, &option)) { -+ return -ENODEV; -+ } -+ ubicom32vfb_setup(option); -+#endif /* MODULE */ -+ -+ ret = platform_driver_register(&ubicom32vfb_platform_driver); -+ -+#ifdef CONFIG_FB_UBICOM32_VIRTUAL_NOAUTO -+ return ret; -+#else -+ if (!ret) { -+ ubicom32vfb_platform_device = platform_device_alloc(DRIVER_NAME, 0); -+ -+ if (ubicom32vfb_platform_device) -+ ret = platform_device_add(ubicom32vfb_platform_device); -+ else -+ ret = -ENOMEM; -+ -+ if (ret) { -+ platform_device_put(ubicom32vfb_platform_device); -+ platform_driver_unregister(&ubicom32vfb_platform_driver); -+ } -+ } -+ -+ return ret; -+#endif -+} -+module_init(ubicom32vfb_init); -+ -+/* -+ * ubicom32vfb_exit -+ */ -+static void __exit ubicom32vfb_exit(void) -+{ -+ platform_device_unregister(ubicom32vfb_platform_device); -+ 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 -@@ -887,6 +887,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 -@@ -131,6 +131,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 -+ * (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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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"); -+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 -@@ -67,6 +67,11 @@ - #define FLAT_DATA_ALIGN (sizeof(void *)) - #endif - -+#ifndef ARCH_FLAT_ALIGN -+#undef FLAT_DATA_ALIGN -+#define FLAT_DATA_ALIGN ARCH_FLAT_ALIGN -+#endif -+ - #define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */ - #define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */ - -@@ -436,6 +441,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; -@@ -521,6 +527,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; - } - - /* -@@ -535,6 +542,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 -@@ -553,7 +566,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), FLAT_DATA_ALIGN); - len = PAGE_ALIGN(len); - down_write(¤t->mm->mmap_sem); - realdatastart = do_mmap(0, 0, len, -@@ -572,6 +585,7 @@ static int load_flat_file(struct linux_b - datapos = ALIGN(realdatastart + - MAX_SHARED_LIBS * sizeof(unsigned long), - FLAT_DATA_ALIGN); -+ //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); -@@ -600,7 +614,11 @@ static int load_flat_file(struct linux_b - memp_size = len; - } 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), FLAT_DATA_ALIGN); -+#ifdef ARCH_FLAT_ALIGN_TEXT -+ /* Reserve space for the text alignment. */ -+ len += FLAT_DATA_ALIGN; -+#endif - len = PAGE_ALIGN(len); - down_write(¤t->mm->mmap_sem); - textpos = do_mmap(0, 0, len, -@@ -616,10 +634,17 @@ static int load_flat_file(struct linux_b - goto err; - } - -+ memp = textpos; -+#ifdef ARCH_FLAT_ALIGN_TEXT -+ textpos = ALIGN(textpos + sizeof(struct flat_hdr), FLAT_DATA_ALIGN) - sizeof(struct flat_hdr); -+#endif - realdatastart = textpos + ntohl(hdr->data_start); - datapos = ALIGN(realdatastart + - MAX_SHARED_LIBS * sizeof(unsigned long), - FLAT_DATA_ALIGN); -+// datapos = realdatastart + ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN); -+// reloc = (unsigned long *) (textpos + ntohl(hdr->reloc_start) + -+// ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN)); - - reloc = (unsigned long *) - (datapos + (ntohl(hdr->reloc_start) - text_len)); -@@ -659,7 +684,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; -@@ -672,6 +697,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, FLAT_DATA_ALIGN) != start_code); -+#endif - end_code = textpos + text_len; - if (id == 0) { - current->mm->start_code = start_code; -@@ -800,6 +828,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/resource.h -+++ b/include/asm-generic/resource.h -@@ -69,13 +69,16 @@ - /* - * boot-time rlimit defaults for the init task: - */ -+#ifndef CONFIG_ELF_CORE -+#define CONFIG_USER_ELF_CORE_SIZE 0 -+#endif - #define INIT_RLIMITS \ - { \ - [RLIMIT_CPU] = { RLIM_INFINITY, RLIM_INFINITY }, \ - [RLIMIT_FSIZE] = { RLIM_INFINITY, RLIM_INFINITY }, \ - [RLIMIT_DATA] = { RLIM_INFINITY, RLIM_INFINITY }, \ - [RLIMIT_STACK] = { _STK_LIM, _STK_LIM_MAX }, \ -- [RLIMIT_CORE] = { 0, RLIM_INFINITY }, \ -+ [RLIMIT_CORE] = { CONFIG_USER_ELF_CORE_SIZE, RLIM_INFINITY }, \ - [RLIMIT_RSS] = { RLIM_INFINITY, RLIM_INFINITY }, \ - [RLIMIT_NPROC] = { 0, 0 }, \ - [RLIMIT_NOFILE] = { INR_OPEN, INR_OPEN }, \ ---- 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/fb.h -+++ b/include/linux/fb.h -@@ -151,6 +151,10 @@ struct dentry; - #define FB_ACCEL_PROSAVAGE_DDR 0x8d /* S3 ProSavage DDR */ - #define FB_ACCEL_PROSAVAGE_DDRK 0x8e /* S3 ProSavage DDR-K */ - -+#define FB_ACCEL_UBICOM32 0x0100 /* Ubicom32 */ -+#define FB_ACCEL_UBICOM32_VFB 0x0101 /* Ubicom32 VFB */ -+#define FB_ACCEL_UBICOM32_PLIO80 0x0102 /* Ubicom32 PLIO80 */ -+ - struct fb_fix_screeninfo { - char id[16]; /* identification string eg "TT Builtin" */ - unsigned long smem_start; /* Start of frame buffer mem */ ---- a/include/linux/if_ppp.h -+++ b/include/linux/if_ppp.h -@@ -114,14 +114,14 @@ struct pppol2tp_ioc_stats { - __u16 tunnel_id; /* redundant */ - __u16 session_id; /* if zero, get tunnel stats */ - __u32 using_ipsec:1; /* valid only for session_id == 0 */ -- aligned_u64 tx_packets; -- aligned_u64 tx_bytes; -- aligned_u64 tx_errors; -- aligned_u64 rx_packets; -- aligned_u64 rx_bytes; -- aligned_u64 rx_seq_discards; -- aligned_u64 rx_oos_packets; -- aligned_u64 rx_errors; -+ __u64 tx_packets; -+ __u64 tx_bytes; -+ __u64 tx_errors; -+ __u64 rx_packets; -+ __u64 rx_bytes; -+ __u64 rx_seq_discards; -+ __u64 rx_oos_packets; -+ __u64 rx_errors; - }; - - #define ifr__name b.ifr_ifrn.ifrn_name ---- a/include/linux/oprofile.h -+++ b/include/linux/oprofile.h -@@ -99,6 +99,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 -@@ -167,6 +167,9 @@ - /* MAX3100 */ - #define PORT_MAX3100 86 - -+/* Ubicom32 */ -+#define PORT_UBI32_UARTTIO 87 -+ - #ifdef __KERNEL__ - - #include ---- a/include/linux/slab.h -+++ b/include/linux/slab.h -@@ -317,4 +317,14 @@ static inline void *kzalloc_node(size_t - return kmalloc_node(size, flags | __GFP_ZERO, node); - } - -+struct kmem_cache_size_info { -+ unsigned short page; -+ unsigned short order; -+}; -+ -+/* -+ * get info on all the memory allocated by slab for this named cache -+ */ -+extern int kmem_cache_block_info(char *name, struct kmem_cache_size_info *data, int max_data); -+ - #endif /* _LINUX_SLAB_H */ ---- a/init/Kconfig -+++ b/init/Kconfig -@@ -865,6 +865,12 @@ config ELF_CORE - help - Enable support for generating core dumps. Disabling saves about 4k. - -+config USER_ELF_CORE_SIZE -+ int "user core dump size (10MB to 32MB)" -+ range 10485760 33554432 -+ default 16777216 -+ depends on ELF_CORE -+ - config PCSPKR_PLATFORM - bool "Enable PC-Speaker support" if EMBEDDED - depends on ALPHA || X86 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES ---- a/kernel/module.c -+++ b/kernel/module.c -@@ -2688,6 +2688,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)); -@@ -2840,8 +2843,12 @@ void print_modules(void) - printk("Modules linked in:"); - /* Most callers should already have preempt disabled, but make sure */ - preempt_disable(); -- list_for_each_entry_rcu(mod, &modules, list) -+ list_for_each_entry_rcu(mod, &modules, list) { - printk(" %s%s", mod->name, module_flags(mod, buf)); -+#ifdef ARCH_OOPS_MODULE_EXTRA -+ ARCH_OOPS_MODULE_EXTRA(mod); -+#endif -+ } - preempt_enable(); - if (last_unloaded_module[0]) - printk(" [last unloaded: %s]", last_unloaded_module); ---- a/kernel/sched_clock.c -+++ b/kernel/sched_clock.c -@@ -38,8 +38,7 @@ - */ - unsigned long long __attribute__((weak)) sched_clock(void) - { -- return (unsigned long long)(jiffies - INITIAL_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 -@@ -621,7 +621,7 @@ config FRAME_POINTER - bool "Compile the kernel with frame pointers" - depends on DEBUG_KERNEL && \ - (CRIS || M68K || M68KNOMMU || FRV || UML || \ -- AVR32 || SUPERH || BLACKFIN || MN10300) || \ -+ AVR32 || SUPERH || BLACKFIN || MN10300 || UBICOM32) || \ - ARCH_WANT_FRAME_POINTERS - default y if (DEBUG_INFO && UML) || ARCH_WANT_FRAME_POINTERS - help ---- a/mm/Makefile -+++ b/mm/Makefile -@@ -38,3 +38,5 @@ obj-$(CONFIG_SMP) += allocpercpu.o - endif - obj-$(CONFIG_QUICKLIST) += quicklist.o - obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o -+ -+CFLAGS_slab.o := $(PROFILING) -O2 ---- a/mm/slab.c -+++ b/mm/slab.c -@@ -4100,6 +4100,68 @@ out: - - #ifdef CONFIG_SLABINFO - -+ -+/* -+ * get info on all the memory allocated by slab for this named cache -+ */ -+int kmem_cache_block_info(char *name, struct kmem_cache_size_info *data, int max_data) -+{ -+ int res = 0; -+ int found = 0; -+ int node; -+ struct kmem_cache *cachep; -+ struct kmem_list3 *l3; -+ struct slab *slabp; -+ -+ /* Find the cache in the chain of caches. */ -+ mutex_lock(&cache_chain_mutex); -+ list_for_each_entry(cachep, &cache_chain, next) { -+ if (strcmp(cachep->name, name) == 0) { -+ found = 1; -+ break; -+ } -+ } -+ mutex_unlock(&cache_chain_mutex); -+ if (!found) { -+ return 0; -+ } -+ for_each_online_node(node) { -+ l3 = cachep->nodelists[node]; -+ if (!l3) -+ continue; -+ if (res >= max_data) -+ break; -+ check_irq_on(); -+ spin_lock_irq(&l3->list_lock); -+ -+ list_for_each_entry(slabp, &l3->slabs_full, list) { -+ if (res >= max_data) -+ break; -+ data[res].page = ((unsigned int)slabp->s_mem >> PAGE_SHIFT) & 0xffff; -+ data[res].order = cachep->gfporder; -+ res++; -+ } -+ list_for_each_entry(slabp, &l3->slabs_partial, list) { -+ if (res >= max_data) -+ break; -+ data[res].page = ((unsigned int)slabp->s_mem >> PAGE_SHIFT) & 0xffff; -+ data[res].order = cachep->gfporder; -+ res++; -+ } -+ list_for_each_entry(slabp, &l3->slabs_free, list) { -+ if (res >= max_data) -+ break; -+ data[res].page = ((unsigned int)slabp->s_mem >> PAGE_SHIFT) & 0xffff; -+ data[res].order = cachep->gfporder; -+ res++; -+ } -+ -+ spin_unlock_irq(&l3->list_lock); -+ } -+ -+ return res; -+} -+ - static void print_slabinfo_header(struct seq_file *m) - { - /* ---- a/scripts/mod/file2alias.c -+++ b/scripts/mod/file2alias.c -@@ -774,6 +774,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 -@@ -82,6 +82,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/ atmel/ -+ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ 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,42 @@ -+# 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_CAPTURE -+ bool "Generic Capture Support" -+ default n -+ help -+ Use this option to support ADCs which don't require special drivers. -+ -+config SND_UBI32_AUDIO_GENERIC -+ bool "Generic Playback Support" -+ default n -+ help -+ Use this option to support DACs which don't require special drivers. -+ -+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,41 @@ -+# -+# 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 . -+# -+# 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-generic-capture-objs := ubi32-generic-capture.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_GENERIC_CAPTURE) += snd-ubi32-generic-capture.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,583 @@ -+/* -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#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 */ -+ -+/* -+ * The dB scale for the Cirrus Logic cs4350. The output range is from -+ * -127.5 dB to 0 dB. -+ */ -+static const DECLARE_TLV_DB_SCALE(snd_ubi32_cs4350_db, -12750, 50, 0); -+ -+#define ubi32_cs4350_mute_info snd_ctl_boolean_stereo_info -+ -+/* -+ * Private data for cs4350 chip -+ */ -+struct ubi32_cs4350_priv { -+ /* -+ * The current volume settings -+ */ -+ uint8_t volume[2]; -+ -+ /* -+ * Bitmask of mutes MSB (unused, ..., unused, right_ch, left_ch) LSB -+ */ -+ uint8_t mute; -+ -+ /* -+ * Lock to protect this struct because callbacks are not atomic. -+ */ -+ spinlock_t lock; -+}; -+ -+/* -+ * The info for the cs4350. The volume currently has one channel, -+ * and 255 possible settings. -+ */ -+static int ubi32_cs4350_volume_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = 2; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = 255; // 8 bits in cirrus logic cs4350 volume register -+ return 0; -+} -+ -+static int ubi32_cs4350_volume_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol); -+ struct ubi32_cs4350_priv *cs4350_priv; -+ unsigned long flags; -+ -+ cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); -+ -+ spin_lock_irqsave(&cs4350_priv->lock, flags); -+ -+ ucontrol->value.integer.value[0] = cs4350_priv->volume[0]; -+ ucontrol->value.integer.value[1] = cs4350_priv->volume[1]; -+ -+ spin_unlock_irqrestore(&cs4350_priv->lock, flags); -+ -+ return 0; -+} -+ -+static int ubi32_cs4350_volume_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol); -+ struct i2c_client *client = (struct i2c_client *)ubi32_priv->client; -+ struct ubi32_cs4350_priv *cs4350_priv; -+ unsigned long flags; -+ int ret, changed; -+ char send[2]; -+ uint8_t volume_reg_value_left, volume_reg_value_right; -+ -+ changed = 0; -+ -+ cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); -+ volume_reg_value_left = 255 - (ucontrol->value.integer.value[0] & 0xFF); -+ volume_reg_value_right = 255 - (ucontrol->value.integer.value[1] & 0xFF); -+ -+#if SND_UBI32_DEBUG -+ snd_printk(KERN_INFO "Setting volume: writing %d,%d to CS4350 volume registers\n", volume_reg_value_left, volume_reg_value_right); -+#endif -+ spin_lock_irqsave(&cs4350_priv->lock, flags); -+ -+ if (cs4350_priv->volume[0] != ucontrol->value.integer.value[0]) { -+ send[0] = 0x05; // left channel -+ send[1] = volume_reg_value_left; -+ ret = i2c_master_send(client, send, 2); -+ if (ret != 2) { -+ snd_printk(KERN_ERR "Failed to set channel A volume on CS4350\n"); -+ return changed; -+ } -+ cs4350_priv->volume[0] = ucontrol->value.integer.value[0]; -+ changed = 1; -+ } -+ -+ if (cs4350_priv->volume[1] != ucontrol->value.integer.value[1]) { -+ send[0] = 0x06; // right channel -+ send[1] = volume_reg_value_right; -+ ret = i2c_master_send(client, send, 2); -+ if (ret != 2) { -+ snd_printk(KERN_ERR "Failed to set channel B volume on CS4350\n"); -+ return changed; -+ } -+ cs4350_priv->volume[1] = ucontrol->value.integer.value[1]; -+ changed = 1; -+ } -+ -+ spin_unlock_irqrestore(&cs4350_priv->lock, flags); -+ -+ return changed; -+} -+ -+static struct snd_kcontrol_new ubi32_cs4350_volume __devinitdata = { -+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | -+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, -+ .name = "PCM Playback Volume", -+ .info = ubi32_cs4350_volume_info, -+ .get = ubi32_cs4350_volume_get, -+ .put = ubi32_cs4350_volume_put, -+ .tlv.p = snd_ubi32_cs4350_db, -+}; -+ -+static int ubi32_cs4350_mute_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol); -+ struct ubi32_cs4350_priv *cs4350_priv; -+ unsigned long flags; -+ -+ cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); -+ -+ spin_lock_irqsave(&cs4350_priv->lock, flags); -+ -+ ucontrol->value.integer.value[0] = cs4350_priv->mute & 1; -+ ucontrol->value.integer.value[1] = (cs4350_priv->mute & (1 << 1)) ? 1 : 0; -+ -+ spin_unlock_irqrestore(&cs4350_priv->lock, flags); -+ -+ return 0; -+} -+ -+static int ubi32_cs4350_mute_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol); -+ struct i2c_client *client = (struct i2c_client *)ubi32_priv->client; -+ struct ubi32_cs4350_priv *cs4350_priv; -+ unsigned long flags; -+ int ret, changed; -+ char send[2]; -+ char recv[1]; -+ uint8_t mute; -+ -+ changed = 0; -+ -+ cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); -+ -+ spin_lock_irqsave(&cs4350_priv->lock, flags); -+ -+ if ((cs4350_priv->mute & 1) != ucontrol->value.integer.value[0]) { -+ send[0] = 0x04; -+ ret = i2c_master_send(client, send, 1); -+ if (ret != 1) { -+ snd_printk(KERN_ERR "Failed to write to mute register: channel 0\n"); -+ return changed; -+ } -+ -+ ret = i2c_master_recv(client, recv, 1); -+ if (ret != 1) { -+ snd_printk(KERN_ERR "Failed to read mute register: channel 0\n"); -+ return changed; -+ } -+ -+ mute = recv[0]; -+ -+ if (ucontrol->value.integer.value[0]) { -+ cs4350_priv->mute |= 1; -+ mute &= ~(1 << 4); -+#if SND_UBI32_DEBUG -+ snd_printk(KERN_INFO "Unmuted channel A\n"); -+#endif -+ } else { -+ cs4350_priv->mute &= ~1; -+ mute |= (1 << 4); -+#if SND_UBI32_DEBUG -+ snd_printk(KERN_INFO "Muted channel A\n"); -+#endif -+ } -+ -+ send[0] = 0x04; -+ send[1] = mute; -+ ret = i2c_master_send(client, send, 2); -+ if (ret != 2) { -+ snd_printk(KERN_ERR "Failed to set channel A mute on CS4350\n"); -+ return changed; -+ } -+ changed = 1; -+ } -+ -+ if (((cs4350_priv->mute & 2) >> 1) != ucontrol->value.integer.value[1]) { -+ send[0] = 0x04; -+ ret = i2c_master_send(client, send, 1); -+ if (ret != 1) { -+ snd_printk(KERN_ERR "Failed to write to mute register: channel 1\n"); -+ return changed; -+ } -+ -+ ret = i2c_master_recv(client, recv, 1); -+ if (ret != 1) { -+ snd_printk(KERN_ERR "Failed to read mute register: channel 1\n"); -+ return changed; -+ } -+ -+ mute = recv[0]; -+ -+ if (ucontrol->value.integer.value[1]) { -+ cs4350_priv->mute |= (1 << 1); -+ mute &= ~(1 << 3); -+#if SND_UBI32_DEBUG -+ snd_printk(KERN_INFO "Unmuted channel B\n"); -+#endif -+ } else { -+ cs4350_priv->mute &= ~(1 << 1); -+ mute |= (1 << 3); -+#if SND_UBI32_DEBUG -+ snd_printk(KERN_INFO "Muted channel B\n"); -+#endif -+ } -+ -+ send[0] = 0x04; -+ send[1] = mute; -+ ret = i2c_master_send(client, send, 2); -+ if (ret != 2) { -+ snd_printk(KERN_ERR "Failed to set channel A mute on CS4350\n"); -+ return changed; -+ } -+ changed = 1; -+ } -+ -+ spin_unlock_irqrestore(&cs4350_priv->lock, flags); -+ -+ return changed; -+} -+ -+static struct snd_kcontrol_new ubi32_cs4350_mute __devinitdata = { -+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, -+ .name = "PCM Playback Switch", -+ .info = ubi32_cs4350_mute_info, -+ .get = ubi32_cs4350_mute_get, -+ .put = ubi32_cs4350_mute_put, -+}; -+ -+/* -+ * snd_ubi32_cs4350_free -+ * Card private data free function -+ */ -+void snd_ubi32_cs4350_free(struct snd_card *card) -+{ -+ struct ubi32_snd_priv *ubi32_priv; -+ struct ubi32_cs4350_priv *cs4350_priv; -+ -+ ubi32_priv = card->private_data; -+ cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv); -+ if (cs4350_priv) { -+ kfree(cs4350_priv); -+ } -+} -+ -+/* -+ * 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 0 dB -+ * Channel B volume set to 0 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; -+ struct ubi32_cs4350_priv *cs4350_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; -+ ubi32_priv->client = client; -+ -+ /* -+ * Create our private data structure -+ */ -+ cs4350_priv = kzalloc(sizeof(struct ubi32_cs4350_priv), GFP_KERNEL); -+ if (!cs4350_priv) { -+ snd_card_free(card); -+ return -ENOMEM; -+ } -+ snd_ubi32_priv_set_drv(ubi32_priv, cs4350_priv); -+ spin_lock_init(&cs4350_priv->lock); -+ -+ /* -+ * Initial volume is set to max by probe function -+ */ -+ cs4350_priv->volume[0] = 0xFF; -+ cs4350_priv->volume[1] = 0xFF; -+ -+ /* -+ * The CS4350 starts off unmuted (bit set = not muted) -+ */ -+ cs4350_priv->mute = 3; -+ -+ /* -+ * 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"); -+ snprintf(card->longname, sizeof(card->longname), -+ "%s at sendirq=%d.%d recvirq=%d.%d regs=%p", -+ card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx, -+ ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr); -+ -+ snd_card_set_dev(card, &client->dev); -+ -+ /* -+ * Set up the mixer components -+ */ -+ err = snd_ctl_add(card, snd_ctl_new1(&ubi32_cs4350_volume, ubi32_priv)); -+ if (err) { -+ snd_printk(KERN_WARNING "Failed to add volume mixer control\n"); -+ } -+ err = snd_ctl_add(card, snd_ctl_new1(&ubi32_cs4350_mute, ubi32_priv)); -+ if (err) { -+ snd_printk(KERN_WARNING "Failed to add mute mixer control\n"); -+ } -+ -+ /* -+ * Register the sound card -+ */ -+ if ((err = snd_card_register(card)) != 0) { -+ snd_printk(KERN_WARNING "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,996 @@ -+/* -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#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 *iod = (struct ubicom32_io_port *)RD; -+ struct ubicom32_io_port *ioe = (struct ubicom32_io_port *)RE; -+ 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; -+ if (!ioa->function) { -+ ioa->function = 3; -+ } -+ return 0; -+ -+ case UBI32_CS4384_MCLK_PWM_1: -+ ioc->function |= 2; -+ ioc->ctl0 |= ctl0 << 16; -+ ioc->ctl2 = ctlx; -+ if (!ioe->function) { -+ ioe->function = 3; -+ } -+ return 0; -+ -+ case UBI32_CS4384_MCLK_PWM_2: -+ ioh->ctl0 |= ctl0; -+ ioh->ctl1 = ctlx; -+ if (!iod->function) { -+ iod->function = 3; -+ } -+ 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 device -+ */ -+ pdev = client->dev.platform_data; -+ if (!pdev) { -+ return -ENODEV; -+ } -+ -+ /* -+ * pdev->dev.platform_data is ubi32-pcm platform_data -+ */ -+ pdata = audio_device_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"); -+ snprintf(card->longname, sizeof(card->longname), -+ "%s at sendirq=%d.%d recvirq=%d.%d regs=%p", -+ card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx, -+ ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr); -+ -+ 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,166 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#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) -+{ -+ /* -+ * 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"); -+ snprintf(card->longname, sizeof(card->longname), -+ "%s at sendirq=%d.%d recvirq=%d.%d regs=%p", -+ card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx, -+ ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr); -+ -+ 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-generic-capture.c -@@ -0,0 +1,167 @@ -+/* -+ * sound/ubicom32/ubi32-generic-capture.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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "ubi32.h" -+ -+#define DRIVER_NAME "snd-ubi32-generic-capture" -+ -+/* -+ * Module properties -+ */ -+static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ -+ -+/* -+ * Card private data free function -+ */ -+void snd_ubi32_generic_capture_free(struct snd_card *card) -+{ -+ /* -+ * 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_capture_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-C", THIS_MODULE, sizeof(struct ubi32_snd_priv)); -+ -+ if (card == NULL) { -+ return -ENOMEM; -+ } -+ -+ card->private_free = snd_ubi32_generic_capture_free; /* Not sure if correct */ -+ ubi32_priv = card->private_data; -+ -+ /* -+ * Initialize the snd_card's private data structure -+ */ -+ ubi32_priv->card = card; -+ ubi32_priv->is_capture = 1; -+ -+ /* -+ * 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-C"); -+ strcpy(card->shortname, "Ubi32-Generic-C"); -+ snprintf(card->longname, sizeof(card->longname), -+ "%s at sendirq=%d.%d recvirq=%d.%d regs=%p", -+ card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx, -+ ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr); -+ -+ 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_capture_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_capture_driver = { -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = snd_ubi32_generic_capture_probe, -+ .remove = __devexit_p(snd_ubi32_generic_capture_remove), -+}; -+ -+/* -+ * snd_ubi32_generic_capture_init -+ */ -+static int __init snd_ubi32_generic_capture_init(void) -+{ -+ return platform_driver_register(&snd_ubi32_generic_capture_driver); -+} -+module_init(snd_ubi32_generic_capture_init); -+ -+/* -+ * snd_ubi32_generic_capture_exit -+ */ -+static void __exit snd_ubi32_generic_capture_exit(void) -+{ -+ platform_driver_unregister(&snd_ubi32_generic_capture_driver); -+} -+module_exit(snd_ubi32_generic_capture_exit); -+ -+/* -+ * Module properties -+ */ -+//#if defined(CONFIG_SND_UBI32_AUDIO_I2C) -+//MODULE_ALIAS("i2c:snd-ubi32"); -+//#endif -+MODULE_AUTHOR("Patrick Tjin"); -+MODULE_DESCRIPTION("Driver for Ubicom32 audio devices"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/sound/ubicom32/ubi32.h -@@ -0,0 +1,102 @@ -+/* -+ * 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 . -+ */ -+ -+#ifndef _UBI32_H -+#define _UBI32_H -+ -+#define SND_UBI32_DEBUG 0 // Debug flag -+ -+#include -+#include -+#include -+#include -+ -+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; -+ -+ /* -+ * capture (1) or playback (0) -+ */ -+ int is_capture; -+ /* -+ * 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 audio_regs *ar; -+ struct audio_dev_regs *adr; -+ u32 irq_idx; -+ 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); -+ + musb_writew(ep->regs, MUSB_TXCSR, txcsr); + } else { + txcsr = MUSB_CSR0_H_SETUPPKT | MUSB_CSR0_TXPKTRDY; +@@ -223,6 +227,8 @@ musb_start_urb(struct musb *musb, int is + break; + default: /* bulk, interrupt */ + /* 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; + } +@@ -342,13 +348,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)); + } + } + +@@ -556,7 +562,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 ---- /dev/null -+++ b/sound/ubicom32/ubi32-pcm.c -@@ -0,0 +1,711 @@ -+/* -+ * 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 . -+ * -+ * Ubicom32 implementation derived from (with many thanks): -+ * arch/m68knommu -+ * arch/blackfin -+ * arch/parisc -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#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; -+ ubi32_priv->ar->int_req |= (1 << ubi32_priv->irq_idx); -+ ubicom32_set_interrupt(ubi32_priv->tx_irq); -+} -+ -+static snd_pcm_uframes_t snd_ubi32_pcm_pointer(struct snd_pcm_substream *substream) -+{ -+ -+ struct ubi32_snd_priv *ubi32_priv = snd_pcm_substream_chip(substream); -+ struct audio_dev_regs *adr = ubi32_priv->adr; -+ 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 (!adr->primary_os_buffer_ptr) { -+ /* -+ * If primary_os_buffer_ptr 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)adr->primary_os_buffer_ptr; -+ frames = bytes_to_frames(runtime, read_pos - ubi32_rd->dma_buffer); -+ if (frames == runtime->buffer_size) { -+ frames = 0; -+ } -+ return frames; -+} + if (unlikely(do_flush)) + musb_h_flush_rxfifo(hw_ep, csr); + else { +@@ -590,6 +600,7 @@ 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); +@@ -604,7 +615,18 @@ musb_rx_reinit(struct musb *musb, struct + */ + if (csr & MUSB_TXCSR_DMAMODE) + musb_writew(ep->regs, MUSB_TXCSR, MUSB_TXCSR_DMAMODE); + -+/* -+ * Audio trigger -+ */ -+static int snd_ubi32_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -+{ -+ struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data; -+ struct audio_dev_regs *adr = ubi32_priv->adr; -+ struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; -+ int ret = 0; ++#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)); + -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "snd_ubi32_pcm_trigger cmd=%d=", cmd); ++ musb_h_flush_rxfifo(ep, MUSB_RXCSR_CLRDATATOG); +#endif -+ -+ if (adr->command != 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; + + /* scrub all previous state, clearing toggle */ + } else { +@@ -1138,8 +1160,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; + } -+ -+ /* -+ * Set interrupt flag to indicate that we interrupted audio device -+ * to send a command -+ */ -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "START\n"); ++#ifdef CONFIG_USB_SERIAL_SIERRAWIRELESS ++ printk(KERN_DEBUG "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, ++ dma ? ", dma" : ""); +#endif -+ /* -+ * Ready the DMA transfer -+ */ -+ ubi32_rd->period_ptr = ubi32_rd->dma_buffer; -+ -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "trigger period_ptr=%lx\n", (unsigned long)ubi32_rd->period_ptr); +#endif -+ adr->dma_xfer_requests[0].ptr = (void *)ubi32_rd->period_ptr; -+ adr->dma_xfer_requests[0].ctr = ubi32_rd->period_size; -+ adr->dma_xfer_requests[0].active = 1; + urb = next_urb(qh); + + -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "xfer_request 0 ptr=0x%x ctr=%u\n", ubi32_rd->period_ptr, ubi32_rd->period_size); + musb_ep_select(mbase, epnum); + tx_csr = musb_readw(epio, MUSB_TXCSR); + +@@ -1180,9 +1212,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 -+ -+ ubi32_rd->period_ptr += ubi32_rd->period_size; -+ adr->dma_xfer_requests[1].ptr = (void *)ubi32_rd->period_ptr; -+ adr->dma_xfer_requests[1].ctr = ubi32_rd->period_size; -+ adr->dma_xfer_requests[1].active = 1; -+ -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "xfer_request 1 ptr=0x%x ctr=%u\n", ubi32_rd->period_ptr, ubi32_rd->period_size); + return; + } + +@@ -1353,8 +1390,14 @@ void musb_host_tx(struct musb *musb, u8 + qh->segsize = length; + + 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_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); ++ MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY); +#endif + -+ /* -+ * Tell the VP that we want to begin playback by filling in the -+ * command field and then interrupting the audio VP -+ */ -+ adr->int_flags |= AUDIO_INT_FLAG_COMMAND; -+ adr->command = AUDIO_CMD_START; -+ snd_ubi32_vp_int_set(substream->pcm); -+ break; -+ -+ case SNDRV_PCM_TRIGGER_STOP: + } + + +@@ -1414,7 +1457,11 @@ static void musb_bulk_rx_nak_timeout(str + + /* clear nak timeout bit */ + rx_csr = musb_readw(epio, MUSB_RXCSR); ++#ifndef CONFIG_UBICOM32 + rx_csr |= MUSB_RXCSR_H_WZC_BITS; ++#else ++ rx_csr &= ~MUSB_RXCSR_H_WZC_BITS; ++#endif + rx_csr &= ~MUSB_RXCSR_DATAERROR; + musb_writew(epio, MUSB_RXCSR, rx_csr); + +@@ -1483,6 +1530,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 + -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "STOP\n"); + DBG(5, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", + epnum, rx_csr, urb->actual_length, + dma ? dma->actual_len : 0); +@@ -1521,8 +1575,15 @@ void musb_host_rx(struct musb *musb, u8 + return; + } + musb_ep_select(mbase, epnum); ++#ifndef CONFIG_UBICOM32 + rx_csr |= MUSB_RXCSR_H_WZC_BITS; + rx_csr &= ~MUSB_RXCSR_DATAERROR; ++#else ++ /* NEED TO EVALUATE CHANGE */ ++ rx_csr &= ~MUSB_RXCSR_H_WZC_BITS; ++ rx_csr &= ~MUSB_RXCSR_DATAERROR; ++// musb_writew(epio, MUSB_RXCSR, (~(MUSB_RXCSR_H_WZC_BITS))| MUSB_RXCSR_H_REQPKT); ++#endif + musb_writew(epio, MUSB_RXCSR, rx_csr); + + goto finish; +@@ -1579,8 +1640,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)) { +@@ -1610,7 +1676,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 +@@ -1823,7 +1889,11 @@ static int musb_schedule( + } else if (hw_ep->out_qh != NULL) + continue; + ++#ifndef CONFIG_UBICOM32 + if (hw_ep == musb->bulk_ep) ++#else ++ if ((hw_ep == musb->bulk_ep_in) || (hw_ep == musb->bulk_ep_out)) /* Ubicom */ +#endif + continue; + + if (is_in) +@@ -1836,7 +1906,14 @@ static int musb_schedule( + best_end = epnum; + } + } + -+ /* -+ * Tell the VP that we want to stop playback by filling in the -+ * command field and then interrupting the audio VP -+ */ -+ adr->int_flags |= AUDIO_INT_FLAG_COMMAND; -+ adr->command = AUDIO_CMD_STOP; -+ snd_ubi32_vp_int_set(substream->pcm); -+ break; ++#ifdef CONFIG_UBICOM32 ++ if (((best_diff >= qh->maxpacket)) && ((qh->type == USB_ENDPOINT_XFER_BULK) && (!is_in))) ++ best_end = -1; ++#endif + -+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* 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) +@@ -1858,6 +1935,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; +@@ -1869,6 +1962,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; + -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "PAUSE_PUSH\n"); + qh->hw_ep = hw_ep; + qh->hep->hcpriv = qh; + if (idle) +@@ -1975,6 +2075,15 @@ static int musb_urb_enqueue( + /* ISO always uses logarithmic encoding */ + interval = min_t(u8, epd->bInterval, 16); + 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 + -+ /* -+ * Tell the VP that we want to pause playback by filling in the -+ * command field and then interrupting the audio VP -+ */ -+ adr->int_flags |= AUDIO_INT_FLAG_COMMAND; -+ adr->command = AUDIO_CMD_PAUSE; -+ snd_ubi32_vp_int_set(substream->pcm); -+ break; -+ -+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -+ -+#ifdef CONFIG_SND_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 -+ */ -+ adr->int_flags |= AUDIO_INT_FLAG_COMMAND; -+ adr->command = AUDIO_CMD_RESUME; -+ snd_ubi32_vp_int_set(substream->pcm); -+ break; + 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 +@@ -58,6 +58,7 @@ static inline void writesb(const void __ + + #ifndef CONFIG_BLACKFIN + ++#ifndef CONFIG_UBICOM32 + /* NOTE: these offsets are all in bytes */ + + static inline u16 musb_readw(const void __iomem *addr, unsigned offset) +@@ -72,7 +73,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 ++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; ++} + -+ default: -+ snd_printk(KERN_WARNING "Unhandled trigger\n"); -+ ret = -EINVAL; -+ break; -+ } ++static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data) ++{ ++ usb_tio_write_u16((u32)(addr + offset), data); ++} + -+ return ret; ++static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) ++{ ++ usb_tio_write_u8((u32)(addr + offset), data); +} + -+/* -+ * Prepare to transfer an audio stream to the codec -+ */ -+static int snd_ubi32_pcm_prepare(struct snd_pcm_substream *substream) ++static inline void musb_read_int_status(u8_t *int_usb, u16_t *int_tx, u16_t *int_rx) +{ -+ /* -+ * Configure registers and setup the runtime instance for DMA transfers -+ */ -+ struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data; -+ struct audio_dev_regs *adr = ubi32_priv->adr; ++ return usb_tio_read_int_status(int_usb, int_tx, int_rx); ++} ++#endif /* CONFIG_UBICOM32 */ + + #ifdef CONFIG_USB_TUSB6010 + +@@ -106,7 +137,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); } +--- a/drivers/usb/musb/musb_regs.h ++++ b/drivers/usb/musb/musb_regs.h +@@ -167,6 +167,7 @@ + (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ + | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) + + -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "snd_ubi32_pcm_prepare: sending STOP command to audio device\n"); -+#endif + /* RXCSR in Peripheral and Host mode */ + #define MUSB_RXCSR_AUTOCLEAR 0x8000 + #define MUSB_RXCSR_DMAENAB 0x2000 +--- a/drivers/video/backlight/Kconfig ++++ b/drivers/video/backlight/Kconfig +@@ -93,6 +93,63 @@ config LCD_HP700 + If you have an HP Jornada 700 series handheld (710/720/728) + say Y to enable LCD control driver. + ++config LCD_UBICOM32POWER ++ tristate "Ubicom LCD power Driver" ++ depends on LCD_CLASS_DEVICE && UBICOM32 ++ default n ++ help ++ If you have a Ubicom32 based system with an LCD panel that requires ++ power control, say Y to enable the power control driver for it. + -+ /* -+ * Make sure the audio device is stopped -+ */ ++config LCD_UBICOM32 ++ tristate "Ubicom Backlight Driver" ++ depends on LCD_CLASS_DEVICE && UBICOM32 ++ default n ++ help ++ This driver takes care of initialization of LCD panels with ++ built in controllers. + -+ /* -+ * Set interrupt flag to indicate that we interrupted audio device -+ * to send a command -+ */ -+ adr->int_flags |= AUDIO_INT_FLAG_COMMAND; -+ adr->command = AUDIO_CMD_STOP; -+ snd_ubi32_vp_int_set(substream->pcm); ++menu "Ubicom32 LCD Panel Support" ++ depends on UBICOM32 && LCD_UBICOM32 + -+ return 0; -+} ++config LCD_UBICOM32_TFT2N0369E_P ++ bool "TFT2N0369E (Portrait)" ++ default n ++ help ++ Support for TFT2N0369 in portrait mode + -+/* -+ * Allocate DMA buffers from preallocated memory. -+ * Preallocation was done in snd_ubi32_pcm_new() -+ */ -+static int snd_ubi32_pcm_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 audio_dev_regs *adr = ubi32_priv->adr; -+ struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data; ++config LCD_UBICOM32_TFT2N0369E_L ++ bool "TFT2N0369E (Landscape)" ++ default n ++ help ++ Support for TFT2N0369 in landscape mode + -+ /* -+ * 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 (ret < 0) { -+ return ret; -+ } ++config LCD_UBICOM32_CFAF240320KTTS ++ bool "CFAF240320KTTS" ++ default n ++ help ++ Support for CFAF240320KTTS + -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "snd_ubi32_pcm_hw_params\n"); -+#endif ++config LCD_UBICOM32_CFAF240320KTTS_180 ++ bool "CFAF240320KTTS (180 rotation)" ++ default n ++ help ++ Support for CFAF240320KTTS rotated 180 degrees + -+ if (!(adr->channel_mask & (1 << params_channels(hw_params)))) { -+ snd_printk(KERN_INFO "snd_ubi32_pcm_hw_params unsupported number of channels %d mask %08x\n", params_channels(hw_params), adr->channel_mask); -+ return -EINVAL; -+ } ++config LCD_UBICOM32_CFAF240320D ++ bool "CFAF240320D" ++ default n ++ help ++ Support for CFAF240320D + -+ 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; -+ } -+ } ++config LCD_UBICOM32_CFAF320240F ++ bool "CFAF320240F" ++ default n ++ help ++ Support for CFAF320240F + -+ 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; -+ } -+ } ++endmenu + -+ 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; -+ } -+ } + # + # Backlight + # +@@ -229,3 +286,11 @@ config BACKLIGHT_SAHARA + help + If you have a Tabletkiosk Sahara Touch-iT, say y to enable the + backlight driver. + -+ if (adr->command != AUDIO_CMD_NONE) { -+ snd_printk(KERN_WARNING "snd_ubi32_pcm_hw_params: tio busy\n"); -+ return -EAGAIN; -+ } ++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 +@@ -9,6 +9,9 @@ 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_UBICOM32POWER) += ubicom32lcdpower.o ++obj-$(CONFIG_LCD_UBICOM32) += ubicom32lcd.o + + obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o + obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o +@@ -24,4 +27,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 +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -609,6 +609,50 @@ 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. ++ You can configure video memory by using kernel command line parameters, for example: ++ video=ubicomfb:vram_size=512,init_value=0xffff + -+ if (params_format(hw_params) == SNDRV_PCM_FORMAT_S16_LE) { -+ adr->flags |= CMD_START_FLAG_LE; -+ } else { -+ adr->flags &= ~CMD_START_FLAG_LE; -+ } -+ adr->channels = params_channels(hw_params); -+ adr->sample_rate = params_rate(hw_params); -+ adr->command = AUDIO_CMD_SETUP; -+ adr->int_flags |= AUDIO_INT_FLAG_COMMAND; -+ snd_ubi32_vp_int_set(substream->pcm); ++config FB_UBICOM32_PLIO80 ++ tristate "Ubicom32 80 Bus PLIO 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 ++ select UBICOM32_PLIO ++ help ++ This is a framebuffer device driver for the Ubicom32 architecture. ++ You can configure the xres, yres and vram size (in kilobytes) by using ++ kernel command line parameters, for example: ++ video=ubicom32vfb:xres=320,yres=240,vram_size=512 + -+ /* -+ * Wait for the command to complete -+ */ -+ while (adr->command != AUDIO_CMD_NONE) { -+ udelay(1); -+ } ++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. ++ You can configure the xres, yres and vram size (in kilobytes) by using ++ kernel command line parameters, for example: ++ video=ubicom32vfb:xres=320,yres=240,vram_size=512 + -+ /* -+ * Put the DMA info into the DMA descriptor that we will -+ * use to do transfers to our audio VP "hardware" -+ */ ++config FB_UBICOM32_VIRTUAL_NOAUTO ++ bool "Do not automatically load" ++ depends on FB_UBICOM32_VIRTUAL ++ help ++ Select this option to prevent the VFB from automatically loading at boot. + + config FB_STI + tristate "HP STI frame buffer device support" +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -136,6 +136,10 @@ obj-$(CONFIG_FB_BF54X_LQ043) += bf54x- + obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o + obj-$(CONFIG_FB_MX3) += mx3fb.o + ++obj-$(CONFIG_FB_UBICOM32) += ubicom32fb.o ++obj-$(CONFIG_FB_UBICOM32_PLIO80) += ubicom32plio80.o ++obj-$(CONFIG_FB_UBICOM32_VIRTUAL) += ubicom32vfb.o ++ + # the test framebuffer is last + obj-$(CONFIG_FB_VIRTUAL) += vfb.o + +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -887,6 +887,19 @@ config WATCHDOG_RIO + machines. The watchdog timeout period is normally one minute but + can be changed with a boot-time parameter. + ++# Ubicom32 + -+ /* -+ * Mark both DMA transfers as not ready/inactive -+ */ -+ adr->dma_xfer_requests[0].active = 0; -+ adr->dma_xfer_requests[1].active = 0; ++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. + -+ /* -+ * 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); ++ To compile this driver as a module, choose M here: the ++ module will be called ubi32_wdt. + -+ /* -+ * Get the period size -+ */ -+ ubi32_rd->period_size = params_period_bytes(hw_params); + # XTENSA Architecture + + # +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -131,6 +131,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 + -+#ifdef CONFIG_SND_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 ret=%d\n", ubi32_rd->dma_buffer, ubi32_rd->dma_buffer_end, ret); + # XTENSA Architecture + + # Architecture Independant +--- a/fs/binfmt_flat.c ++++ b/fs/binfmt_flat.c +@@ -67,6 +67,11 @@ + #define FLAT_DATA_ALIGN (sizeof(void *)) + #endif + ++#ifndef ARCH_FLAT_ALIGN ++#undef FLAT_DATA_ALIGN ++#define FLAT_DATA_ALIGN ARCH_FLAT_ALIGN +#endif + -+ return ret; -+} + #define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */ + #define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */ + +@@ -436,6 +441,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; +@@ -521,6 +527,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; + } + + /* +@@ -535,6 +542,12 @@ static int load_flat_file(struct linux_b + * it all together. + */ + if ((flags & (FLAT_FLAG_RAM|FLAT_FLAG_GZIP)) == 0) { + -+/* -+ * This is the reverse of snd_ubi32_pcm_hw_params -+ */ -+static int snd_ubi32_pcm_hw_free(struct snd_pcm_substream *substream) -+{ -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "snd_ubi32_pcm_hw_free\n"); ++#ifdef ARCH_FLAT_ALIGN_TEXT ++ printk("Unable to mmap rom with ARCH alignment requirements\n"); ++ ret = -ENOEXEC; ++ goto err; +#endif -+ return snd_pcm_lib_free_pages(substream); -+} -+ -+/* -+ * Audio virtual peripheral capabilities (capture and playback are identical) -+ */ -+static struct snd_pcm_hardware snd_ubi32_pcm_hw = -+{ -+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | -+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), -+ .buffer_bytes_max = (64*1024), -+ .period_bytes_min = 64, -+ .period_bytes_max = 8184,//8184,//8176, -+ .periods_min = 2, -+ .periods_max = 255, -+ .fifo_size = 0, // THIS IS IGNORED BY ALSA -+}; -+ -+/* -+ * We fill this in later -+ */ -+static struct snd_pcm_hw_constraint_list ubi32_pcm_rates; -+ -+/* -+ * snd_ubi32_pcm_close -+ */ -+static int snd_ubi32_pcm_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; -+ -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "snd_ubi32_pcm_close\n"); + /* + * this should give us a ROM ptr, but if it doesn't we don't + * really care +@@ -553,7 +566,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), FLAT_DATA_ALIGN); + len = PAGE_ALIGN(len); + down_write(¤t->mm->mmap_sem); + realdatastart = do_mmap(0, 0, len, +@@ -572,6 +585,7 @@ static int load_flat_file(struct linux_b + datapos = ALIGN(realdatastart + + MAX_SHARED_LIBS * sizeof(unsigned long), + FLAT_DATA_ALIGN); ++ //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); +@@ -600,7 +614,11 @@ static int load_flat_file(struct linux_b + memp_size = len; + } 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), FLAT_DATA_ALIGN); ++#ifdef ARCH_FLAT_ALIGN_TEXT ++ /* Reserve space for the text alignment. */ ++ len += FLAT_DATA_ALIGN; +#endif -+ -+ substream->runtime->private_data = NULL; -+ -+ kfree(ubi32_rd); -+ -+ return 0; -+} -+ -+/* -+ * snd_ubi32_pcm_open -+ */ -+static int snd_ubi32_pcm_open(struct snd_pcm_substream *substream) -+{ -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ struct ubi32_snd_runtime_data *ubi32_rd; -+ int ret = 0; -+ -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "ubi32 pcm open\n"); + len = PAGE_ALIGN(len); + down_write(¤t->mm->mmap_sem); + textpos = do_mmap(0, 0, len, +@@ -616,10 +634,17 @@ static int load_flat_file(struct linux_b + goto err; + } + ++ memp = textpos; ++#ifdef ARCH_FLAT_ALIGN_TEXT ++ textpos = ALIGN(textpos + sizeof(struct flat_hdr), FLAT_DATA_ALIGN) - sizeof(struct flat_hdr); +#endif -+ -+ /* Associate capabilities with component */ -+ runtime->hw = snd_ubi32_pcm_hw; -+ -+ /* -+ * Inform ALSA about constraints of the audio device -+ */ -+ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &ubi32_pcm_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; -+ -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "snd_ubi32_pcm_open returned 0\n"); + realdatastart = textpos + ntohl(hdr->data_start); + datapos = ALIGN(realdatastart + + MAX_SHARED_LIBS * sizeof(unsigned long), + FLAT_DATA_ALIGN); ++// datapos = realdatastart + ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN); ++// reloc = (unsigned long *) (textpos + ntohl(hdr->reloc_start) + ++// ALIGN(MAX_SHARED_LIBS * sizeof(unsigned long), ARCH_FLAT_ALIGN)); + + reloc = (unsigned long *) + (datapos + (ntohl(hdr->reloc_start) - text_len)); +@@ -659,7 +684,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; +@@ -672,6 +697,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, FLAT_DATA_ALIGN) != start_code); +#endif -+ -+ return 0; -+out: -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "snd_ubi32_pcm_open returned %d\n", ret); + end_code = textpos + text_len; + if (id == 0) { + current->mm->start_code = start_code; +@@ -800,6 +828,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/resource.h ++++ b/include/asm-generic/resource.h +@@ -69,13 +69,16 @@ + /* + * boot-time rlimit defaults for the init task: + */ ++#ifndef CONFIG_ELF_CORE ++#define CONFIG_USER_ELF_CORE_SIZE 0 +#endif + #define INIT_RLIMITS \ + { \ + [RLIMIT_CPU] = { RLIM_INFINITY, RLIM_INFINITY }, \ + [RLIMIT_FSIZE] = { RLIM_INFINITY, RLIM_INFINITY }, \ + [RLIMIT_DATA] = { RLIM_INFINITY, RLIM_INFINITY }, \ + [RLIMIT_STACK] = { _STK_LIM, _STK_LIM_MAX }, \ +- [RLIMIT_CORE] = { 0, RLIM_INFINITY }, \ ++ [RLIMIT_CORE] = { CONFIG_USER_ELF_CORE_SIZE, RLIM_INFINITY }, \ + [RLIMIT_RSS] = { RLIM_INFINITY, RLIM_INFINITY }, \ + [RLIMIT_NPROC] = { 0, 0 }, \ + [RLIMIT_NOFILE] = { INR_OPEN, INR_OPEN }, \ +--- 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/fb.h ++++ b/include/linux/fb.h +@@ -151,6 +151,10 @@ struct dentry; + #define FB_ACCEL_PROSAVAGE_DDR 0x8d /* S3 ProSavage DDR */ + #define FB_ACCEL_PROSAVAGE_DDRK 0x8e /* S3 ProSavage DDR-K */ + ++#define FB_ACCEL_UBICOM32 0x0100 /* Ubicom32 */ ++#define FB_ACCEL_UBICOM32_VFB 0x0101 /* Ubicom32 VFB */ ++#define FB_ACCEL_UBICOM32_PLIO80 0x0102 /* Ubicom32 PLIO80 */ + -+ return ret; -+} + struct fb_fix_screeninfo { + char id[16]; /* identification string eg "TT Builtin" */ + unsigned long smem_start; /* Start of frame buffer mem */ +--- a/include/linux/if_ppp.h ++++ b/include/linux/if_ppp.h +@@ -114,14 +114,14 @@ struct pppol2tp_ioc_stats { + __u16 tunnel_id; /* redundant */ + __u16 session_id; /* if zero, get tunnel stats */ + __u32 using_ipsec:1; /* valid only for session_id == 0 */ +- aligned_u64 tx_packets; +- aligned_u64 tx_bytes; +- aligned_u64 tx_errors; +- aligned_u64 rx_packets; +- aligned_u64 rx_bytes; +- aligned_u64 rx_seq_discards; +- aligned_u64 rx_oos_packets; +- aligned_u64 rx_errors; ++ __u64 tx_packets; ++ __u64 tx_bytes; ++ __u64 tx_errors; ++ __u64 rx_packets; ++ __u64 rx_bytes; ++ __u64 rx_seq_discards; ++ __u64 rx_oos_packets; ++ __u64 rx_errors; + }; + + #define ifr__name b.ifr_ifrn.ifrn_name +--- a/include/linux/oprofile.h ++++ b/include/linux/oprofile.h +@@ -99,6 +99,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 +@@ -167,6 +167,9 @@ + /* MAX3100 */ + #define PORT_MAX3100 86 + ++/* Ubicom32 */ ++#define PORT_UBI32_UARTTIO 87 + -+static struct snd_pcm_ops snd_ubi32_pcm_ops = { -+ .open = snd_ubi32_pcm_open, /* Open */ -+ .close = snd_ubi32_pcm_close, /* Close */ -+ .ioctl = snd_pcm_lib_ioctl, /* Generic IOCTL handler */ -+ .hw_params = snd_ubi32_pcm_hw_params, /* Hardware parameters/capabilities */ -+ .hw_free = snd_ubi32_pcm_hw_free, /* Free function for hw_params */ -+ .prepare = snd_ubi32_pcm_prepare, -+ .trigger = snd_ubi32_pcm_trigger, -+ .pointer = snd_ubi32_pcm_pointer, + #ifdef __KERNEL__ + + #include +--- a/include/linux/slab.h ++++ b/include/linux/slab.h +@@ -317,4 +317,14 @@ static inline void *kzalloc_node(size_t + return kmalloc_node(size, flags | __GFP_ZERO, node); + } + ++struct kmem_cache_size_info { ++ unsigned short page; ++ unsigned short order; +}; + +/* -+ * Interrupt handler that gets called when the audio device -+ * interrupts Linux ++ * get info on all the memory allocated by slab for this named cache + */ -+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 audio_dev_regs *adr = ubi32_priv->adr; -+ struct snd_pcm_substream *substream; -+ struct ubi32_snd_runtime_data *ubi32_rd; -+ int dma_to_fill = 0; -+ -+ /* -+ * Check to see if the interrupt is for us -+ */ -+ if (!(ubi32_priv->ar->int_status & (1 << ubi32_priv->irq_idx))) { -+ return IRQ_NONE; -+ } -+ -+ /* -+ * Clear the interrupt -+ */ -+ ubi32_priv->ar->int_status &= ~(1 << ubi32_priv->irq_idx); -+ -+ /* -+ * We only have one stream since we don't mix. Therefore -+ * we don't need to search through substreams. -+ */ -+ if (ubi32_priv->is_capture) { -+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; -+ } else { -+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; -+ } -+ -+ if (!substream->runtime) { -+ snd_printk(KERN_WARNING "No runtime data\n"); -+ return IRQ_NONE; -+ } ++extern int kmem_cache_block_info(char *name, struct kmem_cache_size_info *data, int max_data); + -+ ubi32_rd = substream->runtime->private_data; + #endif /* _LINUX_SLAB_H */ +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -865,6 +865,12 @@ config ELF_CORE + help + Enable support for generating core dumps. Disabling saves about 4k. + ++config USER_ELF_CORE_SIZE ++ int "user core dump size (10MB to 32MB)" ++ range 10485760 33554432 ++ default 16777216 ++ depends on ELF_CORE + -+#ifdef CONFIG_SND_DEBUG -+ snd_printk(KERN_INFO "Ubi32 ALSA interrupt\n"); + config PCSPKR_PLATFORM + bool "Enable PC-Speaker support" if EMBEDDED + depends on ALPHA || X86 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -2688,6 +2688,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 -+ -+ if (ubi32_rd == NULL) { -+ snd_printk(KERN_WARNING "No private data\n"); -+ return IRQ_NONE; -+ } -+ -+ // Check interrupt cause -+ if (0) { -+ // Handle the underflow case -+ } else if ((adr->status & AUDIO_STATUS_PLAY_DMA0_REQUEST) || -+ (adr->status & AUDIO_STATUS_PLAY_DMA1_REQUEST)) { -+ if (adr->status & AUDIO_STATUS_PLAY_DMA0_REQUEST) { -+ dma_to_fill = 0; -+ adr->status &= ~AUDIO_STATUS_PLAY_DMA0_REQUEST; -+ } else if (adr->status & AUDIO_STATUS_PLAY_DMA1_REQUEST) { -+ dma_to_fill = 1; -+ adr->status &= ~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; -+ } -+ adr->dma_xfer_requests[dma_to_fill].ptr = (void *)ubi32_rd->period_ptr; -+ adr->dma_xfer_requests[dma_to_fill].ctr = ubi32_rd->period_size; -+ adr->dma_xfer_requests[dma_to_fill].active = 1; -+#ifdef CONFIG_SND_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); + /* Taints info */ + if (mod->taints) + seq_printf(m, " %s", module_flags(mod, buf)); +@@ -2840,8 +2843,12 @@ void print_modules(void) + printk("Modules linked in:"); + /* Most callers should already have preempt disabled, but make sure */ + preempt_disable(); +- list_for_each_entry_rcu(mod, &modules, list) ++ list_for_each_entry_rcu(mod, &modules, list) { + printk(" %s%s", mod->name, module_flags(mod, buf)); ++#ifdef ARCH_OOPS_MODULE_EXTRA ++ ARCH_OOPS_MODULE_EXTRA(mod); +#endif -+ adr->int_flags |= 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); -+} + preempt_enable(); + if (last_unloaded_module[0]) + printk(" [last unloaded: %s]", last_unloaded_module); +--- a/kernel/sched_clock.c ++++ b/kernel/sched_clock.c +@@ -38,8 +38,7 @@ + */ + unsigned long long __attribute__((weak)) sched_clock(void) + { +- return (unsigned long long)(jiffies - INITIAL_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 +@@ -621,7 +621,7 @@ config FRAME_POINTER + bool "Compile the kernel with frame pointers" + depends on DEBUG_KERNEL && \ + (CRIS || M68K || M68KNOMMU || FRV || UML || \ +- AVR32 || SUPERH || BLACKFIN || MN10300) || \ ++ AVR32 || SUPERH || BLACKFIN || MN10300 || UBICOM32) || \ + ARCH_WANT_FRAME_POINTERS + default y if (DEBUG_INFO && UML) || ARCH_WANT_FRAME_POINTERS + help +--- a/mm/Makefile ++++ b/mm/Makefile +@@ -38,3 +38,5 @@ obj-$(CONFIG_SMP) += allocpercpu.o + endif + obj-$(CONFIG_QUICKLIST) += quicklist.o + obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o + -+#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}; ++CFLAGS_slab.o := $(PROFILING) -O2 +--- a/mm/slab.c ++++ b/mm/slab.c +@@ -4100,6 +4100,68 @@ out: + + #ifdef CONFIG_SLABINFO + + +/* -+ * snd_ubi32_pcm_probe ++ * get info on all the memory allocated by slab for this named cache + */ -+int __devinit snd_ubi32_pcm_probe(struct ubi32_snd_priv *ubi32_priv, struct platform_device *pdev) ++int kmem_cache_block_info(char *name, struct kmem_cache_size_info *data, int max_data) +{ -+ 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 audio_dev_regs *adr; -+ struct resource *res_adr; -+ struct resource *res_irq_tx; -+ struct resource *res_irq_rx; -+ struct ubi32pcm_platform_data *pdata; -+ -+ pdata = pdev->dev.platform_data; -+ if (!pdata) { -+ return -ENODEV; -+ } ++ int res = 0; ++ int found = 0; ++ int node; ++ struct kmem_cache *cachep; ++ struct kmem_list3 *l3; ++ struct slab *slabp; + -+ /* -+ * Get our resources, adr is the hardware driver base address -+ * and the tx and rx irqs are used to communicate with the -+ * hardware driver. -+ */ -+ res_adr = platform_get_resource(pdev, IORESOURCE_MEM, AUDIO_MEM_RESOURCE); -+ res_irq_tx = platform_get_resource(pdev, IORESOURCE_IRQ, AUDIO_TX_IRQ_RESOURCE); -+ res_irq_rx = platform_get_resource(pdev, IORESOURCE_IRQ, AUDIO_RX_IRQ_RESOURCE); -+ if (!res_adr || !res_irq_tx || !res_irq_rx) { -+ snd_printk(KERN_WARNING "Could not get resources"); -+ return -ENODEV; ++ /* Find the cache in the chain of caches. */ ++ mutex_lock(&cache_chain_mutex); ++ list_for_each_entry(cachep, &cache_chain, next) { ++ if (strcmp(cachep->name, name) == 0) { ++ found = 1; ++ break; ++ } + } -+ -+ ubi32_priv->ar = (struct audio_regs *)res_adr->start; -+ ubi32_priv->tx_irq = res_irq_tx->start; -+ ubi32_priv->rx_irq = res_irq_rx->start; -+ ubi32_priv->irq_idx = pdata->inst_num; -+ ubi32_priv->adr = &(ubi32_priv->ar->adr[pdata->inst_num]); -+ -+ /* -+ * Check the version -+ */ -+ adr = ubi32_priv->adr; -+ if (adr->version != AUDIO_DEV_REGS_VERSION) { -+ snd_printk(KERN_WARNING "This audio_dev_reg is not compatible with this driver\n"); -+ return -ENODEV; ++ mutex_unlock(&cache_chain_mutex); ++ if (!found) { ++ return 0; + } ++ for_each_online_node(node) { ++ l3 = cachep->nodelists[node]; ++ if (!l3) ++ continue; ++ if (res >= max_data) ++ break; ++ check_irq_on(); ++ spin_lock_irq(&l3->list_lock); + -+ /* -+ * 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 < adr->n_sample_rates; j++) { -+ if (rates[i] == adr->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; -+ } -+ } ++ list_for_each_entry(slabp, &l3->slabs_full, list) { ++ if (res >= max_data) ++ break; ++ data[res].page = ((unsigned int)slabp->s_mem >> PAGE_SHIFT) & 0xffff; ++ data[res].order = cachep->gfporder; ++ res++; + } -+ if (!found) { -+ rate_mask |= SNDRV_PCM_RATE_KNOT; ++ list_for_each_entry(slabp, &l3->slabs_partial, list) { ++ if (res >= max_data) ++ break; ++ data[res].page = ((unsigned int)slabp->s_mem >> PAGE_SHIFT) & 0xffff; ++ data[res].order = cachep->gfporder; ++ res++; + } -+ } -+ -+ snd_ubi32_pcm_hw.rates = rate_mask; -+ snd_ubi32_pcm_hw.rate_min = rate_min; -+ snd_ubi32_pcm_hw.rate_max = rate_max; -+ ubi32_pcm_rates.count = adr->n_sample_rates; -+ ubi32_pcm_rates.list = (unsigned int *)adr->sample_rates; -+ ubi32_pcm_rates.mask = 0; -+ -+ for (i = 0; i < 32; i++) { -+ if (adr->channel_mask & (1 << i)) { -+ if (!snd_ubi32_pcm_hw.channels_min) { -+ snd_ubi32_pcm_hw.channels_min = i; -+ } -+ snd_ubi32_pcm_hw.channels_max = i; ++ list_for_each_entry(slabp, &l3->slabs_free, list) { ++ if (res >= max_data) ++ break; ++ data[res].page = ((unsigned int)slabp->s_mem >> PAGE_SHIFT) & 0xffff; ++ data[res].order = cachep->gfporder; ++ res++; + } -+ } -+ snd_printk(KERN_INFO "Ubi32PCM: channels_min:%u channels_max:%u\n", -+ snd_ubi32_pcm_hw.channels_min, -+ snd_ubi32_pcm_hw.channels_max); -+ -+ if (adr->caps & AUDIONODE_CAP_BE) { -+ snd_ubi32_pcm_hw.formats |= SNDRV_PCM_FMTBIT_S16_BE; -+ } -+ if (adr->caps & AUDIONODE_CAP_LE) { -+ snd_ubi32_pcm_hw.formats |= SNDRV_PCM_FMTBIT_S16_LE; -+ } -+ -+ snd_printk(KERN_INFO "Ubi32PCM: rates:%08x min:%u max:%u count:%d fmts:%016llx (%s)\n", -+ snd_ubi32_pcm_hw.rates, -+ snd_ubi32_pcm_hw.rate_min, -+ snd_ubi32_pcm_hw.rate_max, -+ ubi32_pcm_rates.count, -+ snd_ubi32_pcm_hw.formats, -+ ubi32_priv->is_capture ? "capture" : "playback"); -+ -+ if (ubi32_priv->is_capture) { -+ ret = snd_pcm_new(ubi32_priv->card, "Ubi32 PCM", 0, 0, 1, &pcm); -+ } else { -+ 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; -+ -+ strcpy(pcm->name, "Ubi32-PCM"); -+ -+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, -+ snd_dma_continuous_data(GFP_KERNEL), -+ 45*1024, 64*1024); -+ -+ if (ubi32_priv->is_capture) { -+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ubi32_pcm_ops); -+ } else { -+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ubi32_pcm_ops); ++ spin_unlock_irq(&l3->list_lock); + } + -+ /* -+ * Start up the audio device -+ */ -+ adr->int_flags |= AUDIO_INT_FLAG_COMMAND; -+ adr->command = AUDIO_CMD_ENABLE; -+ snd_ubi32_vp_int_set(pcm); ++ return res; ++} + + static void print_slabinfo_header(struct seq_file *m) + { + /* +--- a/scripts/mod/file2alias.c ++++ b/scripts/mod/file2alias.c +@@ -774,6 +774,15 @@ void handle_moddevtable(struct module *m + + sym->st_value; + } + + /* -+ * Request IRQ ++ * 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. + */ -+ err = request_irq(ubi32_priv->rx_irq, snd_ubi32_pcm_interrupt, IRQF_SHARED | 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; ++ 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; + } + -+ return ret; + 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 +@@ -82,6 +82,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/ atmel/ ++ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ ubicom32/ + obj-$(CONFIG_SND_AOA) += aoa/ + + # This one must be compilable even if sound is configured out