All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v1 00/40] Meta Linux Kernel Port
@ 2012-10-31 16:13 James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 01/40] asm-generic/io.h: remove asm/cacheflush.h include James Hogan
                   ` (40 more replies)
  0 siblings, 41 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

This patchset adds core architecture support to Linux for Imagination's
Meta ATP (Meta 1) and HTP (Meta 2) processor cores. It is primarily an
RFC patchset to get some early feedback about what changes we need to
make, although of course all feedback is most welcome.

The patches are based on v3.6 for now, and can also be found in the
following git tree:
  git://github.com/jahogan/metag-linux.git metag-core

Meta cores are 32-bit, hardware multithreaded, general purpose, embedded
processors which also feature a DSP instruction set, and can be found in
many digital radios. They are capable of running different operating
systems on different hardware threads, for example a digital radio might
run RTOSes for DAB decoding and audio decoding on 3 hardware threads,
and run Linux on the 4th hardware thread to manage the user interface,
networking etc. HTPs are also capable of running SMP Linux on multiple
hardware threads.

Instruction set and architecture overview documents can be found on the
following page (these currently require registration/NDA, a restriction
which was not intended and will shortly be removed):
  http://www.imgtec.com/downloads.asp

James Hogan (40):
  asm-generic/io.h: remove asm/cacheflush.h include
  asm-generic/unistd.h: handle symbol prefixes in cond_syscall
  Add CONFIG_HAVE_64BIT_ALIGNED_STRUCT for taskstats
  trace/ring_buffer: handle 64bit aligned structs
  Revert some of "binfmt_elf: cleanups"
  of/vendor-prefixes: add Imagination Technologies
  metag: Add MAINTAINERS entry
  metag: Boot
  metag: TBX
  metag: Memory management
  metag: Signal handling
  metag: Build infrastructure
  metag: Device tree
  metag: Ptrace
  metag: Time keeping
  metag: Traps
  metag: IRQ handling
  metag: System Calls
  metag: Scheduling/Process management
  metag: Module support
  metag: Atomics, locks and bitops
  metag: Basic documentation
  metag: SMP support
  metag: DMA
  metag: optimised library functions
  metag: Stack unwinding
  metag: various other headers
  metag: Perf
  metag: ftrace support
  scripts/checkstack.pl: Add metag support
  char: don't build rtc or genrtc on METAG
  i8042: don't build on METAG
  parport: don't build on METAG
  musb: don't redefine {read,write}s{l,w,b} on metag
  vga console: don't build on METAG
  metag: OProfile
  metag: Various sysfs drivers
  metag: add JTAG Debug Adapter (DA) support
  tty/metag_da: add metag DA TTY driver
  fs: dafs: Add DAFS filesystem for metag

 Documentation/00-INDEX                             |    2 +
 .../devicetree/bindings/metag/meta-intc.txt        |   92 ++
 .../devicetree/bindings/vendor-prefixes.txt        |    3 +-
 Documentation/kernel-parameters.txt                |    4 +
 Documentation/metag/00-INDEX                       |    2 +
 MAINTAINERS                                        |   10 +
 arch/Kconfig                                       |   16 +
 arch/metag/Kconfig                                 |  325 +++++
 arch/metag/Kconfig.debug                           |   40 +
 arch/metag/Kconfig.soc                             |   58 +
 arch/metag/Makefile                                |  101 ++
 arch/metag/boot/.gitignore                         |    4 +
 arch/metag/boot/Makefile                           |   72 +
 arch/metag/boot/dtb/Makefile                       |   32 +
 arch/metag/boot/dts/skeleton.dts                   |   10 +
 arch/metag/boot/dts/skeleton.dtsi                  |   14 +
 arch/metag/configs/meta1_defconfig                 |   45 +
 arch/metag/configs/meta2_defconfig                 |   45 +
 arch/metag/configs/meta2_smp_defconfig             |   46 +
 arch/metag/include/asm/Kbuild                      |   58 +
 arch/metag/include/asm/atomic.h                    |   53 +
 arch/metag/include/asm/atomic_lnkget.h             |  245 ++++
 arch/metag/include/asm/atomic_lock1.h              |  160 +++
 arch/metag/include/asm/barrier.h                   |   88 ++
 arch/metag/include/asm/bitops.h                    |  135 ++
 arch/metag/include/asm/bug.h                       |   12 +
 arch/metag/include/asm/byteorder.h                 |    1 +
 arch/metag/include/asm/cache.h                     |   23 +
 arch/metag/include/asm/cacheflush.h                |  203 +++
 arch/metag/include/asm/cachepart.h                 |   42 +
 arch/metag/include/asm/checksum.h                  |   92 ++
 arch/metag/include/asm/clock.h                     |   51 +
 arch/metag/include/asm/cmpxchg.h                   |   65 +
 arch/metag/include/asm/cmpxchg_irq.h               |   42 +
 arch/metag/include/asm/cmpxchg_lnkget.h            |   86 ++
 arch/metag/include/asm/cmpxchg_lock1.h             |   48 +
 arch/metag/include/asm/core-sysfs.h                |    9 +
 arch/metag/include/asm/core_reg.h                  |    7 +
 arch/metag/include/asm/cpu.h                       |   14 +
 arch/metag/include/asm/da.h                        |   43 +
 arch/metag/include/asm/delay.h                     |   29 +
 arch/metag/include/asm/div64.h                     |   12 +
 arch/metag/include/asm/dma-mapping.h               |  183 +++
 arch/metag/include/asm/elf.h                       |  131 ++
 arch/metag/include/asm/fixmap.h                    |   99 ++
 arch/metag/include/asm/ftrace.h                    |   23 +
 arch/metag/include/asm/gpio.h                      |    4 +
 arch/metag/include/asm/highmem.h                   |   62 +
 arch/metag/include/asm/hugetlb.h                   |   82 ++
 arch/metag/include/asm/hw_breakpoints.h            |   30 +
 arch/metag/include/asm/hwthread.h                  |   44 +
 arch/metag/include/asm/io.h                        |   96 ++
 arch/metag/include/asm/irq.h                       |   79 +
 arch/metag/include/asm/irq_external.h              |   33 +
 arch/metag/include/asm/irq_internal.h              |   16 +
 arch/metag/include/asm/irqflags.h                  |  100 ++
 arch/metag/include/asm/l2cache.h                   |  268 ++++
 arch/metag/include/asm/linkage.h                   |    7 +
 arch/metag/include/asm/lock.h                      |   17 +
 arch/metag/include/asm/mach/arch.h                 |   81 ++
 arch/metag/include/asm/mman.h                      |   14 +
 arch/metag/include/asm/mmu.h                       |   77 +
 arch/metag/include/asm/mmu_context.h               |  113 ++
 arch/metag/include/asm/mmzone.h                    |   49 +
 arch/metag/include/asm/module.h                    |   39 +
 arch/metag/include/asm/page.h                      |  132 ++
 arch/metag/include/asm/perf_event.h                |    4 +
 arch/metag/include/asm/pgalloc.h                   |   79 +
 arch/metag/include/asm/pgtable.h                   |  370 +++++
 arch/metag/include/asm/processor.h                 |  209 +++
 arch/metag/include/asm/prom.h                      |   23 +
 arch/metag/include/asm/ptrace.h                    |   50 +
 arch/metag/include/asm/resource.h                  |    7 +
 arch/metag/include/asm/setup.h                     |   11 +
 arch/metag/include/asm/sigcontext.h                |   23 +
 arch/metag/include/asm/siginfo.h                   |    8 +
 arch/metag/include/asm/smp.h                       |   31 +
 arch/metag/include/asm/sparsemem.h                 |   16 +
 arch/metag/include/asm/spinlock.h                  |   22 +
 arch/metag/include/asm/spinlock_lnkget.h           |  261 ++++
 arch/metag/include/asm/spinlock_lock1.h            |  184 +++
 arch/metag/include/asm/spinlock_types.h            |   20 +
 arch/metag/include/asm/stacktrace.h                |   20 +
 arch/metag/include/asm/string.h                    |   13 +
 arch/metag/include/asm/swab.h                      |   26 +
 arch/metag/include/asm/switch.h                    |   22 +
 arch/metag/include/asm/syscall.h                   |  104 ++
 arch/metag/include/asm/syscalls.h                  |   67 +
 arch/metag/include/asm/tbx/Kbuild                  |    8 +
 arch/metag/include/asm/tbx/compasm.inc             |   84 ++
 arch/metag/include/asm/tbx/machine.inc             |   24 +
 arch/metag/include/asm/tbx/metac_1_2.inc           |  606 ++++++++
 arch/metag/include/asm/tbx/metac_2_1.inc           | 1290 +++++++++++++++++
 arch/metag/include/asm/tbx/metac_all.inc           |   28 +
 arch/metag/include/asm/tbx/metag.inc               | 1340 ++++++++++++++++++
 arch/metag/include/asm/tbx/metagdsp.inc            |  506 +++++++
 arch/metag/include/asm/tbx/metagtbx.h              | 1492 ++++++++++++++++++++
 arch/metag/include/asm/tcm.h                       |   30 +
 arch/metag/include/asm/thread_info.h               |  157 ++
 arch/metag/include/asm/tlb.h                       |   36 +
 arch/metag/include/asm/tlbflush.h                  |   80 ++
 arch/metag/include/asm/topology.h                  |   53 +
 arch/metag/include/asm/traps.h                     |   46 +
 arch/metag/include/asm/uaccess.h                   |  245 ++++
 arch/metag/include/asm/unistd.h                    |   28 +
 arch/metag/include/asm/user_gateway.h              |   44 +
 arch/metag/kernel/.gitignore                       |    1 +
 arch/metag/kernel/Makefile                         |   40 +
 arch/metag/kernel/asm-offsets.c                    |   14 +
 arch/metag/kernel/cachepart.c                      |  125 ++
 arch/metag/kernel/clock.c                          |   53 +
 arch/metag/kernel/core_reg.c                       |  120 ++
 arch/metag/kernel/cpu/Kconfig                      |   53 +
 arch/metag/kernel/cpu/Makefile                     |    8 +
 arch/metag/kernel/cpu/core-sysfs.c                 |   60 +
 arch/metag/kernel/cpu/counters/Makefile            |    7 +
 arch/metag/kernel/cpu/counters/amacount.c          |  227 +++
 arch/metag/kernel/cpu/counters/cyclecount.c        |  135 ++
 arch/metag/kernel/cpu/counters/perfcount.c         |  154 ++
 arch/metag/kernel/cpu/l2cache-control.c            |  289 ++++
 arch/metag/kernel/cpu/memory-arbiter.c             |  133 ++
 arch/metag/kernel/cpu/write-combiner.c             |  215 +++
 arch/metag/kernel/da.c                             |   24 +
 arch/metag/kernel/devtree.c                        |  100 ++
 arch/metag/kernel/dma.c                            |  521 +++++++
 arch/metag/kernel/ftrace.c                         |  127 ++
 arch/metag/kernel/ftrace_stub.S                    |   76 +
 arch/metag/kernel/head.S                           |   57 +
 arch/metag/kernel/hw_breakpoints.c                 |  102 ++
 arch/metag/kernel/irq.c                            |  277 ++++
 arch/metag/kernel/irq_external.c                   |  886 ++++++++++++
 arch/metag/kernel/irq_internal.c                   |  235 +++
 arch/metag/kernel/kick.c                           |   94 ++
 arch/metag/kernel/machines.c                       |   21 +
 arch/metag/kernel/metag_ksyms.c                    |   80 ++
 arch/metag/kernel/module.c                         |  284 ++++
 arch/metag/kernel/perf_callchain.c                 |   96 ++
 arch/metag/kernel/process.c                        |  487 +++++++
 arch/metag/kernel/ptrace.c                         |  503 +++++++
 arch/metag/kernel/setup.c                          |  585 ++++++++
 arch/metag/kernel/signal.c                         |  340 +++++
 arch/metag/kernel/smp.c                            |  582 ++++++++
 arch/metag/kernel/stacktrace.c                     |  187 +++
 arch/metag/kernel/sys_metag.c                      |  209 +++
 arch/metag/kernel/tbiunexp.S                       |   22 +
 arch/metag/kernel/tcm.c                            |  151 ++
 arch/metag/kernel/time.c                           |  165 +++
 arch/metag/kernel/topology.c                       |   77 +
 arch/metag/kernel/traps.c                          |  967 +++++++++++++
 arch/metag/kernel/user_gateway.S                   |   99 ++
 arch/metag/kernel/vmlinux.lds.S                    |   71 +
 arch/metag/lib/Makefile                            |    7 +
 arch/metag/lib/ashldi3.S                           |   33 +
 arch/metag/lib/ashrdi3.S                           |   33 +
 arch/metag/lib/checksum.c                          |  169 +++
 arch/metag/lib/clear_page.S                        |   17 +
 arch/metag/lib/copy_page.S                         |   20 +
 arch/metag/lib/delay.c                             |   55 +
 arch/metag/lib/div64.S                             |  108 ++
 arch/metag/lib/divsi3.S                            |  100 ++
 arch/metag/lib/ip_fast_csum.S                      |   32 +
 arch/metag/lib/lshrdi3.S                           |   33 +
 arch/metag/lib/memcpy.S                            |  185 +++
 arch/metag/lib/memmove.S                           |  345 +++++
 arch/metag/lib/memset.S                            |   86 ++
 arch/metag/lib/modsi3.S                            |   38 +
 arch/metag/lib/muldi3.S                            |   44 +
 arch/metag/lib/usercopy.c                          | 1341 ++++++++++++++++++
 arch/metag/mm/Kconfig                              |  153 ++
 arch/metag/mm/Makefile                             |   17 +
 arch/metag/mm/extable.c                            |   15 +
 arch/metag/mm/fault.c                              |  240 ++++
 arch/metag/mm/highmem.c                            |  133 ++
 arch/metag/mm/hugetlbpage.c                        |  291 ++++
 arch/metag/mm/init.c                               |  448 ++++++
 arch/metag/mm/ioremap.c                            |   89 ++
 arch/metag/mm/l2cache.c                            |  222 +++
 arch/metag/mm/maccess.c                            |   67 +
 arch/metag/mm/mmu-meta1.c                          |  157 ++
 arch/metag/mm/mmu-meta2.c                          |  208 +++
 arch/metag/mm/numa.c                               |  119 ++
 arch/metag/oprofile/Makefile                       |   11 +
 arch/metag/oprofile/backtrace.c                    |  134 ++
 arch/metag/oprofile/backtrace.h                    |    6 +
 arch/metag/oprofile/op_model_meta12.c              |  244 ++++
 arch/metag/tbx/Makefile                            |   17 +
 arch/metag/tbx/tbicache.c                          |  461 ++++++
 arch/metag/tbx/tbicore.S                           |  137 ++
 arch/metag/tbx/tbictx.S                            |  368 +++++
 arch/metag/tbx/tbictxfpu.S                         |  193 +++
 arch/metag/tbx/tbidefr.S                           |  176 +++
 arch/metag/tbx/tbidspram.S                         |  165 +++
 arch/metag/tbx/tbilogf.S                           |   50 +
 arch/metag/tbx/tbipcx.S                            |  456 ++++++
 arch/metag/tbx/tbiroot.S                           |   89 ++
 arch/metag/tbx/tbisoft.S                           |  239 ++++
 arch/metag/tbx/tbistring.c                         |  148 ++
 arch/metag/tbx/tbitimer.S                          |  210 +++
 arch/openrisc/include/asm/io.h                     |    1 +
 drivers/char/Kconfig                               |    5 +-
 drivers/input/serio/Kconfig                        |    2 +-
 drivers/parport/Kconfig                            |    2 +-
 drivers/tty/Kconfig                                |   13 +
 drivers/tty/Makefile                               |    1 +
 drivers/tty/metag_da.c                             |  503 +++++++
 drivers/usb/musb/musb_io.h                         |    3 +-
 drivers/video/console/Kconfig                      |    2 +-
 fs/Kconfig                                         |    1 +
 fs/Makefile                                        |    1 +
 fs/binfmt_elf.c                                    |    4 +
 fs/dafs/Kconfig                                    |    6 +
 fs/dafs/Makefile                                   |    7 +
 fs/dafs/dafs.h                                     |   80 ++
 fs/dafs/inode.c                                    |  830 +++++++++++
 include/asm-generic/io.h                           |    1 -
 include/asm-generic/unistd.h                       |    9 +-
 kernel/taskstats.c                                 |    3 +-
 kernel/trace/ring_buffer.c                         |    7 +-
 lib/Kconfig.debug                                  |    6 +-
 scripts/checkstack.pl                              |    8 +-
 scripts/recordmcount.pl                            |   12 +
 221 files changed, 30343 insertions(+), 17 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/metag/meta-intc.txt
 create mode 100644 Documentation/metag/00-INDEX
 create mode 100644 arch/metag/Kconfig
 create mode 100644 arch/metag/Kconfig.debug
 create mode 100644 arch/metag/Kconfig.soc
 create mode 100644 arch/metag/Makefile
 create mode 100644 arch/metag/boot/.gitignore
 create mode 100644 arch/metag/boot/Makefile
 create mode 100644 arch/metag/boot/dtb/Makefile
 create mode 100644 arch/metag/boot/dts/skeleton.dts
 create mode 100644 arch/metag/boot/dts/skeleton.dtsi
 create mode 100644 arch/metag/configs/meta1_defconfig
 create mode 100644 arch/metag/configs/meta2_defconfig
 create mode 100644 arch/metag/configs/meta2_smp_defconfig
 create mode 100644 arch/metag/include/asm/Kbuild
 create mode 100644 arch/metag/include/asm/atomic.h
 create mode 100644 arch/metag/include/asm/atomic_lnkget.h
 create mode 100644 arch/metag/include/asm/atomic_lock1.h
 create mode 100644 arch/metag/include/asm/barrier.h
 create mode 100644 arch/metag/include/asm/bitops.h
 create mode 100644 arch/metag/include/asm/bug.h
 create mode 100644 arch/metag/include/asm/byteorder.h
 create mode 100644 arch/metag/include/asm/cache.h
 create mode 100644 arch/metag/include/asm/cacheflush.h
 create mode 100644 arch/metag/include/asm/cachepart.h
 create mode 100644 arch/metag/include/asm/checksum.h
 create mode 100644 arch/metag/include/asm/clock.h
 create mode 100644 arch/metag/include/asm/cmpxchg.h
 create mode 100644 arch/metag/include/asm/cmpxchg_irq.h
 create mode 100644 arch/metag/include/asm/cmpxchg_lnkget.h
 create mode 100644 arch/metag/include/asm/cmpxchg_lock1.h
 create mode 100644 arch/metag/include/asm/core-sysfs.h
 create mode 100644 arch/metag/include/asm/core_reg.h
 create mode 100644 arch/metag/include/asm/cpu.h
 create mode 100644 arch/metag/include/asm/da.h
 create mode 100644 arch/metag/include/asm/delay.h
 create mode 100644 arch/metag/include/asm/div64.h
 create mode 100644 arch/metag/include/asm/dma-mapping.h
 create mode 100644 arch/metag/include/asm/elf.h
 create mode 100644 arch/metag/include/asm/fixmap.h
 create mode 100644 arch/metag/include/asm/ftrace.h
 create mode 100644 arch/metag/include/asm/gpio.h
 create mode 100644 arch/metag/include/asm/highmem.h
 create mode 100644 arch/metag/include/asm/hugetlb.h
 create mode 100644 arch/metag/include/asm/hw_breakpoints.h
 create mode 100644 arch/metag/include/asm/hwthread.h
 create mode 100644 arch/metag/include/asm/io.h
 create mode 100644 arch/metag/include/asm/irq.h
 create mode 100644 arch/metag/include/asm/irq_external.h
 create mode 100644 arch/metag/include/asm/irq_internal.h
 create mode 100644 arch/metag/include/asm/irqflags.h
 create mode 100644 arch/metag/include/asm/l2cache.h
 create mode 100644 arch/metag/include/asm/linkage.h
 create mode 100644 arch/metag/include/asm/lock.h
 create mode 100644 arch/metag/include/asm/mach/arch.h
 create mode 100644 arch/metag/include/asm/mman.h
 create mode 100644 arch/metag/include/asm/mmu.h
 create mode 100644 arch/metag/include/asm/mmu_context.h
 create mode 100644 arch/metag/include/asm/mmzone.h
 create mode 100644 arch/metag/include/asm/module.h
 create mode 100644 arch/metag/include/asm/page.h
 create mode 100644 arch/metag/include/asm/perf_event.h
 create mode 100644 arch/metag/include/asm/pgalloc.h
 create mode 100644 arch/metag/include/asm/pgtable.h
 create mode 100644 arch/metag/include/asm/processor.h
 create mode 100644 arch/metag/include/asm/prom.h
 create mode 100644 arch/metag/include/asm/ptrace.h
 create mode 100644 arch/metag/include/asm/resource.h
 create mode 100644 arch/metag/include/asm/setup.h
 create mode 100644 arch/metag/include/asm/sigcontext.h
 create mode 100644 arch/metag/include/asm/siginfo.h
 create mode 100644 arch/metag/include/asm/smp.h
 create mode 100644 arch/metag/include/asm/sparsemem.h
 create mode 100644 arch/metag/include/asm/spinlock.h
 create mode 100644 arch/metag/include/asm/spinlock_lnkget.h
 create mode 100644 arch/metag/include/asm/spinlock_lock1.h
 create mode 100644 arch/metag/include/asm/spinlock_types.h
 create mode 100644 arch/metag/include/asm/stacktrace.h
 create mode 100644 arch/metag/include/asm/string.h
 create mode 100644 arch/metag/include/asm/swab.h
 create mode 100644 arch/metag/include/asm/switch.h
 create mode 100644 arch/metag/include/asm/syscall.h
 create mode 100644 arch/metag/include/asm/syscalls.h
 create mode 100644 arch/metag/include/asm/tbx/Kbuild
 create mode 100644 arch/metag/include/asm/tbx/compasm.inc
 create mode 100644 arch/metag/include/asm/tbx/machine.inc
 create mode 100644 arch/metag/include/asm/tbx/metac_1_2.inc
 create mode 100644 arch/metag/include/asm/tbx/metac_2_1.inc
 create mode 100644 arch/metag/include/asm/tbx/metac_all.inc
 create mode 100644 arch/metag/include/asm/tbx/metag.inc
 create mode 100644 arch/metag/include/asm/tbx/metagdsp.inc
 create mode 100644 arch/metag/include/asm/tbx/metagtbx.h
 create mode 100644 arch/metag/include/asm/tcm.h
 create mode 100644 arch/metag/include/asm/thread_info.h
 create mode 100644 arch/metag/include/asm/tlb.h
 create mode 100644 arch/metag/include/asm/tlbflush.h
 create mode 100644 arch/metag/include/asm/topology.h
 create mode 100644 arch/metag/include/asm/traps.h
 create mode 100644 arch/metag/include/asm/uaccess.h
 create mode 100644 arch/metag/include/asm/unistd.h
 create mode 100644 arch/metag/include/asm/user_gateway.h
 create mode 100644 arch/metag/kernel/.gitignore
 create mode 100644 arch/metag/kernel/Makefile
 create mode 100644 arch/metag/kernel/asm-offsets.c
 create mode 100644 arch/metag/kernel/cachepart.c
 create mode 100644 arch/metag/kernel/clock.c
 create mode 100644 arch/metag/kernel/core_reg.c
 create mode 100644 arch/metag/kernel/cpu/Kconfig
 create mode 100644 arch/metag/kernel/cpu/Makefile
 create mode 100644 arch/metag/kernel/cpu/core-sysfs.c
 create mode 100644 arch/metag/kernel/cpu/counters/Makefile
 create mode 100644 arch/metag/kernel/cpu/counters/amacount.c
 create mode 100644 arch/metag/kernel/cpu/counters/cyclecount.c
 create mode 100644 arch/metag/kernel/cpu/counters/perfcount.c
 create mode 100644 arch/metag/kernel/cpu/l2cache-control.c
 create mode 100644 arch/metag/kernel/cpu/memory-arbiter.c
 create mode 100644 arch/metag/kernel/cpu/write-combiner.c
 create mode 100644 arch/metag/kernel/da.c
 create mode 100644 arch/metag/kernel/devtree.c
 create mode 100644 arch/metag/kernel/dma.c
 create mode 100644 arch/metag/kernel/ftrace.c
 create mode 100644 arch/metag/kernel/ftrace_stub.S
 create mode 100644 arch/metag/kernel/head.S
 create mode 100644 arch/metag/kernel/hw_breakpoints.c
 create mode 100644 arch/metag/kernel/irq.c
 create mode 100644 arch/metag/kernel/irq_external.c
 create mode 100644 arch/metag/kernel/irq_internal.c
 create mode 100644 arch/metag/kernel/kick.c
 create mode 100644 arch/metag/kernel/machines.c
 create mode 100644 arch/metag/kernel/metag_ksyms.c
 create mode 100644 arch/metag/kernel/module.c
 create mode 100644 arch/metag/kernel/perf_callchain.c
 create mode 100644 arch/metag/kernel/process.c
 create mode 100644 arch/metag/kernel/ptrace.c
 create mode 100644 arch/metag/kernel/setup.c
 create mode 100644 arch/metag/kernel/signal.c
 create mode 100644 arch/metag/kernel/smp.c
 create mode 100644 arch/metag/kernel/stacktrace.c
 create mode 100644 arch/metag/kernel/sys_metag.c
 create mode 100644 arch/metag/kernel/tbiunexp.S
 create mode 100644 arch/metag/kernel/tcm.c
 create mode 100644 arch/metag/kernel/time.c
 create mode 100644 arch/metag/kernel/topology.c
 create mode 100644 arch/metag/kernel/traps.c
 create mode 100644 arch/metag/kernel/user_gateway.S
 create mode 100644 arch/metag/kernel/vmlinux.lds.S
 create mode 100644 arch/metag/lib/Makefile
 create mode 100644 arch/metag/lib/ashldi3.S
 create mode 100644 arch/metag/lib/ashrdi3.S
 create mode 100644 arch/metag/lib/checksum.c
 create mode 100644 arch/metag/lib/clear_page.S
 create mode 100644 arch/metag/lib/copy_page.S
 create mode 100644 arch/metag/lib/delay.c
 create mode 100644 arch/metag/lib/div64.S
 create mode 100644 arch/metag/lib/divsi3.S
 create mode 100644 arch/metag/lib/ip_fast_csum.S
 create mode 100644 arch/metag/lib/lshrdi3.S
 create mode 100644 arch/metag/lib/memcpy.S
 create mode 100644 arch/metag/lib/memmove.S
 create mode 100644 arch/metag/lib/memset.S
 create mode 100644 arch/metag/lib/modsi3.S
 create mode 100644 arch/metag/lib/muldi3.S
 create mode 100644 arch/metag/lib/usercopy.c
 create mode 100644 arch/metag/mm/Kconfig
 create mode 100644 arch/metag/mm/Makefile
 create mode 100644 arch/metag/mm/extable.c
 create mode 100644 arch/metag/mm/fault.c
 create mode 100644 arch/metag/mm/highmem.c
 create mode 100644 arch/metag/mm/hugetlbpage.c
 create mode 100644 arch/metag/mm/init.c
 create mode 100644 arch/metag/mm/ioremap.c
 create mode 100644 arch/metag/mm/l2cache.c
 create mode 100644 arch/metag/mm/maccess.c
 create mode 100644 arch/metag/mm/mmu-meta1.c
 create mode 100644 arch/metag/mm/mmu-meta2.c
 create mode 100644 arch/metag/mm/numa.c
 create mode 100644 arch/metag/oprofile/Makefile
 create mode 100644 arch/metag/oprofile/backtrace.c
 create mode 100644 arch/metag/oprofile/backtrace.h
 create mode 100644 arch/metag/oprofile/op_model_meta12.c
 create mode 100644 arch/metag/tbx/Makefile
 create mode 100644 arch/metag/tbx/tbicache.c
 create mode 100644 arch/metag/tbx/tbicore.S
 create mode 100644 arch/metag/tbx/tbictx.S
 create mode 100644 arch/metag/tbx/tbictxfpu.S
 create mode 100644 arch/metag/tbx/tbidefr.S
 create mode 100644 arch/metag/tbx/tbidspram.S
 create mode 100644 arch/metag/tbx/tbilogf.S
 create mode 100644 arch/metag/tbx/tbipcx.S
 create mode 100644 arch/metag/tbx/tbiroot.S
 create mode 100644 arch/metag/tbx/tbisoft.S
 create mode 100644 arch/metag/tbx/tbistring.c
 create mode 100644 arch/metag/tbx/tbitimer.S
 create mode 100644 drivers/tty/metag_da.c
 create mode 100644 fs/dafs/Kconfig
 create mode 100644 fs/dafs/Makefile
 create mode 100644 fs/dafs/dafs.h
 create mode 100644 fs/dafs/inode.c

-- 
1.7.7.6

^ permalink raw reply	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 01/40] asm-generic/io.h: remove asm/cacheflush.h include
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 02/40] asm-generic/unistd.h: handle symbol prefixes in cond_syscall James Hogan
                   ` (39 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch
  Cc: James Hogan, Chris Zankel, Mike Frysinger, Chen Liqin, Lennox Wu,
	Arnd Bergmann

Including <asm/cacheflush.h> from <asm-generic/io.h> prevents
cacheflush.h being able to use I/O functions like readl and writel due
to circular include dependencies. It doesn't appear as if anything from
cacheflush.h is actually used by the generic io.h, so remove the
include.

I've compile tested a defconfig compilation of blackfin, openrisc (which
needed <asm/pgtable.h> including from it's <asm/io.h> to get the PAGE_*
definitions), and xtensa.

Other architectures which use asm-generic/io.h are score and unicore32,
and looking at their io.h I don't see any obvious problems.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Acked-by: Jonas Bonn <jonas@southpole.se>
Acked-by: Max Filippov <jcmvbkbc@gmail.com>
Acked-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
Cc: Chris Zankel <chris@zankel.net>
Cc: Mike Frysinger <vapier@gentoo.org>
Cc: Chen Liqin <liqin.chen@sunplusct.com>
Cc: Lennox Wu <lennox.wu@gmail.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
This patch is included for completeness as it's a pre-requisite of the
metag port using the generic io.h.

 arch/openrisc/include/asm/io.h |    1 +
 include/asm-generic/io.h       |    1 -
 2 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/openrisc/include/asm/io.h b/arch/openrisc/include/asm/io.h
index 07f5299..7c69139 100644
--- a/arch/openrisc/include/asm/io.h
+++ b/arch/openrisc/include/asm/io.h
@@ -30,6 +30,7 @@
 #define PIO_MASK		0
 
 #include <asm-generic/io.h>
+#include <asm/pgtable.h>
 
 extern void __iomem *__ioremap(phys_addr_t offset, unsigned long size,
 				pgprot_t prot);
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 448303b..616eea5 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -12,7 +12,6 @@
 #define __ASM_GENERIC_IO_H
 
 #include <asm/page.h> /* I/O is all done through memory accesses */
-#include <asm/cacheflush.h>
 #include <linux/types.h>
 
 #ifdef CONFIG_GENERIC_IOMAP
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 02/40] asm-generic/unistd.h: handle symbol prefixes in cond_syscall
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 01/40] asm-generic/io.h: remove asm/cacheflush.h include James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 03/40] Add CONFIG_HAVE_64BIT_ALIGNED_STRUCT for taskstats James Hogan
                   ` (38 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Some architectures have symbol prefixes and set CONFIG_SYMBOL_PREFIX,
but this wasn't taken into account by the generic cond_syscall. It's
easy enough to fix in a generic fashion, so add the symbol prefix to
symbol names in cond_syscall when CONFIG_SYMBOL_PREFIX is set.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Mike Frysinger <vapier@gentoo.org>
---
 include/asm-generic/unistd.h |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h
index 3748ec9..af5c3a6 100644
--- a/include/asm-generic/unistd.h
+++ b/include/asm-generic/unistd.h
@@ -926,7 +926,14 @@ __SYSCALL(__NR_fork, sys_ni_syscall)
  * but it doesn't work on all toolchains, so we just do it by hand
  */
 #ifndef cond_syscall
-#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+#ifdef CONFIG_SYMBOL_PREFIX
+#define __SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX
+#else
+#define __SYMBOL_PREFIX
+#endif
+#define cond_syscall(x) asm(".weak\t" __SYMBOL_PREFIX #x "\n\t" \
+			    ".set\t" __SYMBOL_PREFIX #x "," \
+			    __SYMBOL_PREFIX "sys_ni_syscall")
 #endif
 
 #endif /* __KERNEL__ */
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 03/40] Add CONFIG_HAVE_64BIT_ALIGNED_STRUCT for taskstats
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 01/40] asm-generic/io.h: remove asm/cacheflush.h include James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 02/40] asm-generic/unistd.h: handle symbol prefixes in cond_syscall James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 04/40] trace/ring_buffer: handle 64bit aligned structs James Hogan
                   ` (37 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch
  Cc: James Hogan, Balbir Singh, Ingo Molnar, Andrew Morton,
	Eric Paris, Will Drewry, James Morris

On 64 bit architectures with no efficient unaligned access, taskstats
has to add some padding to a reply to prevent unaligned access warnings.
However this also needs to apply to 32 bit architectures with 64 bit
struct alignment such as metag (which has 64 bit memory accesses).

This is solved by adding a new Kconfig symbol
CONFIG_HAVE_64BIT_ALIGNED_STRUCT which defaults to CONFIG_64BIT, and can
be explicitly selected by CONFIG_METAG. Taskstats then uses this symbol
instead of CONFIG_64BIT to determine whether to add the padding.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Eric Paris <eparis@redhat.com>
Cc: Will Drewry <wad@chromium.org>
Cc: James Morris <james.l.morris@oracle.com>
---
 arch/Kconfig       |   16 ++++++++++++++++
 kernel/taskstats.c |    3 ++-
 2 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 72f2fa1..26eba37 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -93,6 +93,22 @@ config UPROBES
 
 	  If in doubt, say "N".
 
+config HAVE_64BIT_ALIGNED_STRUCT
+	bool
+	default 64BIT
+	help
+	  Some architectures have 64bit struct alignment (64 bit
+	  architectures and some 32 bit architectures which can do
+	  64 bit accesses).
+
+	  This symbol should be selected by an architecture if it has
+	  a 64 bit struct alignment requirement even though it isn't
+	  a 64 bit architecture (64 bit architectures have this on by
+	  default).
+
+	  See Documentation/unaligned-memory-access.txt for more
+	  information on the topic of unaligned memory accesses.
+
 config HAVE_EFFICIENT_UNALIGNED_ACCESS
 	bool
 	help
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index d0a3279..6b26c40 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -355,7 +355,8 @@ static int parse(struct nlattr *na, struct cpumask *mask)
 	return ret;
 }
 
-#if defined(CONFIG_64BIT) && !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+#if defined(CONFIG_HAVE_64BIT_ALIGNED_STRUCT) && \
+	!defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
 #define TASKSTATS_NEEDS_PADDING 1
 #endif
 
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 04/40] trace/ring_buffer: handle 64bit aligned structs
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (2 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 03/40] Add CONFIG_HAVE_64BIT_ALIGNED_STRUCT for taskstats James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 17:59   ` Steven Rostedt
  2012-10-31 16:13   ` James Hogan
                   ` (36 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan, Steven Rostedt, Frederic Weisbecker, Ingo Molnar

Some 32 bit architectures have 64 bit struct alignment (for example
metag which has 64 bit read/write instructions). These require 8 byte
alignment of event data too, so use CONFIG_HAVE_64BIT_ALIGNED_STRUCT
instead of CONFIG_64BIT to decide alignment, and align
buffer_data_page::data accordingly.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
---
 kernel/trace/ring_buffer.c |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 49491fa..15a63d8 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -177,7 +177,8 @@ void tracing_off_permanent(void)
 #define RB_MAX_SMALL_DATA	(RB_ALIGNMENT * RINGBUF_TYPE_DATA_TYPE_LEN_MAX)
 #define RB_EVNT_MIN_SIZE	8U	/* two 32bit words */
 
-#if !defined(CONFIG_64BIT) || defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+#if !defined(CONFIG_HAVE_64BIT_ALIGNED_STRUCT) || \
+	defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
 # define RB_FORCE_8BYTE_ALIGNMENT	0
 # define RB_ARCH_ALIGNMENT		RB_ALIGNMENT
 #else
@@ -185,6 +186,8 @@ void tracing_off_permanent(void)
 # define RB_ARCH_ALIGNMENT		8U
 #endif
 
+#define RB_ALIGN_DATA		__aligned(RB_ARCH_ALIGNMENT)
+
 /* define RINGBUF_TYPE_DATA for 'case RINGBUF_TYPE_DATA:' */
 #define RINGBUF_TYPE_DATA 0 ... RINGBUF_TYPE_DATA_TYPE_LEN_MAX
 
@@ -333,7 +336,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data);
 struct buffer_data_page {
 	u64		 time_stamp;	/* page time stamp */
 	local_t		 commit;	/* write committed index */
-	unsigned char	 data[];	/* data of buffer page */
+	unsigned char	 data[] RB_ALIGN_DATA;	/* data of buffer page */
 };
 
 /*
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 05/40] Revert some of "binfmt_elf: cleanups"
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
@ 2012-10-31 16:13   ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 02/40] asm-generic/unistd.h: handle symbol prefixes in cond_syscall James Hogan
                     ` (39 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan, Mikael Pettersson, Alexander Viro, linux-fsdevel

The commit "binfmt_elf: cleanups"
(f670d0ecda73b7438eec9ed108680bc5f5362ad8) removed an ifndef elf_map but
this breaks compilation on metag architecture which does define elf_map.

This adds the ifndef back in as it was before, but does not affect the
other cleanups made by that patch.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Mikael Pettersson <mikpe@it.uu.se>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
---
 fs/binfmt_elf.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 1b52956..14d1fbd 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -314,6 +314,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	return 0;
 }
 
+#ifndef elf_map
+
 static unsigned long elf_map(struct file *filep, unsigned long addr,
 		struct elf_phdr *eppnt, int prot, int type,
 		unsigned long total_size)
@@ -348,6 +350,8 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
 	return(map_addr);
 }
 
+#endif /* !elf_map */
+
 static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
 {
 	int i, first_idx = -1, last_idx = -1;
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 05/40] Revert some of "binfmt_elf: cleanups"
@ 2012-10-31 16:13   ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan, Mikael Pettersson, Alexander Viro, linux-fsdevel

The commit "binfmt_elf: cleanups"
(f670d0ecda73b7438eec9ed108680bc5f5362ad8) removed an ifndef elf_map but
this breaks compilation on metag architecture which does define elf_map.

This adds the ifndef back in as it was before, but does not affect the
other cleanups made by that patch.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Mikael Pettersson <mikpe@it.uu.se>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
---
 fs/binfmt_elf.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 1b52956..14d1fbd 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -314,6 +314,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	return 0;
 }
 
+#ifndef elf_map
+
 static unsigned long elf_map(struct file *filep, unsigned long addr,
 		struct elf_phdr *eppnt, int prot, int type,
 		unsigned long total_size)
@@ -348,6 +350,8 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
 	return(map_addr);
 }
 
+#endif /* !elf_map */
+
 static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
 {
 	int i, first_idx = -1, last_idx = -1;
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 06/40] of/vendor-prefixes: add Imagination Technologies
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (4 preceding siblings ...)
  2012-10-31 16:13   ` James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-11-01  1:38   ` Rob Herring
  2012-10-31 16:13 ` [RFC PATCH v1 07/40] metag: Add MAINTAINERS entry James Hogan
                   ` (34 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch
  Cc: James Hogan, Grant Likely, Rob Herring, Rob Landley, devicetree-discuss

The "powervr" prefix which is currently described as "Imagination
Technologies" isn't really appropriate for non-PowerVR hardware, so
change the description of "powervr" to "PowerVR (Imagination
Technologies Ltd.)", and add a separate "img" prefix for "Imagination
Technologies Ltd.".

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: devicetree-discuss@lists.ozlabs.org
---
 .../devicetree/bindings/vendor-prefixes.txt        |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index db4d3af..5a94721b 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -24,6 +24,7 @@ gef	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
 hp	Hewlett Packard
 ibm	International Business Machines (IBM)
 idt	Integrated Device Technologies, Inc.
+img	Imagination Technologies Ltd.
 intercontrol	Inter Control Group
 linux	Linux-specific binding
 marvell	Marvell Technology Group Ltd.
@@ -34,7 +35,7 @@ nintendo	Nintendo
 nvidia	NVIDIA
 nxp	NXP Semiconductors
 picochip	Picochip Ltd
-powervr	Imagination Technologies
+powervr	PowerVR (Imagination Technologies Ltd.)
 qcom	Qualcomm, Inc.
 ramtron	Ramtron International
 realtek Realtek Semiconductor Corp.
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 07/40] metag: Add MAINTAINERS entry
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (5 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 06/40] of/vendor-prefixes: add Imagination Technologies James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 08/40] metag: Boot James Hogan
                   ` (33 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add MAINTAINERS entry for the metag architecture. Will and myself will
be maintaining it between us.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 MAINTAINERS |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 9a6c4da..e1d8299 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4530,6 +4530,14 @@ F:	drivers/mtd/
 F:	include/linux/mtd/
 F:	include/mtd/
 
+METAG ARCHITECTURE
+M:	James Hogan <james.hogan@imgtec.com>
+M:	Will Newton <will.newton@imgtec.com>
+S:	Maintained
+F:	arch/metag/
+F:	Documentation/metag/
+F:	Documentation/devicetree/bindings/metag/
+
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
 L:	microblaze-uclinux@itee.uq.edu.au (moderated for non-subscribers)
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 08/40] metag: Boot
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (6 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 07/40] metag: Add MAINTAINERS entry James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 11/40] metag: Signal handling James Hogan
                   ` (32 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add boot code for metag. Due to the multi-threaded nature of Meta it is
not uncommon for an RTOS or bare metal application to be started on
other hardware threads by the bootloader. Since there is a single MMU
switch which affects all threads, the MMU is traditionally configured by
the bootloader prior to starting Linux. The bootloader passes a
structure to Linux which among other things contains information about
memory regions which have been mapped. Linux then assumes control of the
local heap memory region.

A kernel arguments string pointer or a flattened device tree pointer can
be provided in the third argument.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/boot/.gitignore         |    4 +
 arch/metag/include/asm/mach/arch.h |   77 +++++
 arch/metag/include/asm/setup.h     |   11 +
 arch/metag/kernel/head.S           |   45 +++
 arch/metag/kernel/machines.c       |   21 ++
 arch/metag/kernel/setup.c          |  570 ++++++++++++++++++++++++++++++++++++
 6 files changed, 728 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/boot/.gitignore
 create mode 100644 arch/metag/include/asm/mach/arch.h
 create mode 100644 arch/metag/include/asm/setup.h
 create mode 100644 arch/metag/kernel/head.S
 create mode 100644 arch/metag/kernel/machines.c
 create mode 100644 arch/metag/kernel/setup.c

diff --git a/arch/metag/boot/.gitignore b/arch/metag/boot/.gitignore
new file mode 100644
index 0000000..a021da2
--- /dev/null
+++ b/arch/metag/boot/.gitignore
@@ -0,0 +1,4 @@
+vmlinux*
+uImage*
+ramdisk.*
+*.dtb
diff --git a/arch/metag/include/asm/mach/arch.h b/arch/metag/include/asm/mach/arch.h
new file mode 100644
index 0000000..8429c76
--- /dev/null
+++ b/arch/metag/include/asm/mach/arch.h
@@ -0,0 +1,77 @@
+/*
+ * arch/metag/include/asm/mach/arch.h
+ *
+ * Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ * based on the ARM version:
+ *  Copyright (C) 2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _METAG_MACH_ARCH_H_
+#define _METAG_MACH_ARCH_H_
+
+#include <linux/stddef.h>
+
+/**
+ * struct machine_desc - Describes a board controlled by a Meta.
+ * @name:		Board/SoC name.
+ * @dt_compat:		Array of device tree 'compatible' strings.
+ *
+ * @init_early:		Early init callback.
+ * @init_irq:		IRQ init callback for setting up IRQ controllers.
+ * @init_machine:	Arch init callback for setting up devices.
+ * @init_late:		Late init callback.
+ *
+ * This structure is provided by each board which can be controlled by a Meta.
+ * It is chosen by matching the compatible strings in the device tree provided
+ * by the bootloader with the strings in @dt_compat, and sets up any aspects of
+ * the machine that aren't configured with device tree (yet).
+ */
+struct machine_desc {
+	const char		*name;
+	const char		**dt_compat;
+
+	void			(*init_early)(void);
+	void			(*init_irq)(void);
+	void			(*init_machine)(void);
+	void			(*init_late)(void);
+};
+
+/*
+ * Current machine - only accessible during boot.
+ */
+extern struct machine_desc *machine_desc;
+
+/*
+ * Machine type table - also only accessible during boot
+ */
+extern struct machine_desc __arch_info_begin[], __arch_info_end[];
+#define for_each_machine_desc(p)			\
+	for (p = __arch_info_begin; p < __arch_info_end; p++)
+
+static inline struct machine_desc *default_machine_desc(void)
+{
+	/* the default machine is the last one linked in */
+	if (__arch_info_end - 1 < __arch_info_begin)
+		return NULL;
+	return __arch_info_end - 1;
+}
+
+/*
+ * Set of macros to define architecture features.  This is built into
+ * a table by the linker.
+ */
+#define MACHINE_START(_type, _name)			\
+static const struct machine_desc __mach_desc_##_type	\
+__used							\
+__attribute__((__section__(".arch.info.init"))) = {	\
+	.name		= _name,
+
+#define MACHINE_END				\
+};
+
+#endif /* _METAG_MACH_ARCH_H_ */
diff --git a/arch/metag/include/asm/setup.h b/arch/metag/include/asm/setup.h
new file mode 100644
index 0000000..aaba778
--- /dev/null
+++ b/arch/metag/include/asm/setup.h
@@ -0,0 +1,11 @@
+#ifndef _ASM_METAG_SETUP_H
+#define _ASM_METAG_SETUP_H
+
+#include <asm-generic/setup.h>
+
+#ifdef  __KERNEL__
+void per_cpu_trap_init(unsigned long);
+extern void __init dump_machine_table(void);
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_METAG_SETUP_H */
diff --git a/arch/metag/kernel/head.S b/arch/metag/kernel/head.S
new file mode 100644
index 0000000..d0d8352
--- /dev/null
+++ b/arch/metag/kernel/head.S
@@ -0,0 +1,45 @@
+	! Copyright 2005,2006,2007,2009 Imagination Technologies
+
+#include <linux/init.h>
+#include <generated/asm-offsets.h>
+#undef __exit
+
+	__HEAD
+	! Setup the stack and get going into _metag_start_kernel
+	.global	__start
+	.type   __start,function
+__start:
+	! D1Ar1 contains pTBI (ISTAT)
+	! D0Ar2 contains pTBI
+	! D1Ar3 contains __pTBISegs
+	! D0Ar4 contains kernel arglist pointer
+
+	MOVT    D0Re0,#HI(___pTBIs)
+	ADD     D0Re0,D0Re0,#LO(___pTBIs)
+	SETL    [D0Re0],D0Ar2,D1Ar1
+	MOVT    D0Re0,#HI(___pTBISegs)
+	ADD     D0Re0,D0Re0,#LO(___pTBISegs)
+	SETD    [D0Re0],D1Ar3
+	MOV	A0FrP,#0
+	MOV	D0Re0,#0
+	MOV	D1Re0,#0
+	MOV	D1Ar3,#0
+	MOV	D1Ar1,D0Ar4			!Store kernel boot params
+	MOV	D1Ar5,#0
+	MOV	D0Ar6,#0
+#ifdef CONFIG_META_DSP
+	MOV	D0.8,#0
+#endif
+	MOVT    A0StP,#HI(_init_thread_union)
+	ADD	A0StP,A0StP,#LO(_init_thread_union)
+	ADD     A0StP,A0StP,#THREAD_INFO_SIZE
+	MOVT	D1RtP,#HI(_metag_start_kernel)
+	CALL	D1RtP,#LO(_metag_start_kernel)
+	.size	__start,.-__start
+
+	!! Needed by TBX
+	.global	__exit
+	.type   __exit,function
+__exit:
+	XOR     TXENABLE,D0Re0,D0Re0
+	.size	__exit,.-__exit
diff --git a/arch/metag/kernel/machines.c b/arch/metag/kernel/machines.c
new file mode 100644
index 0000000..2a48f55
--- /dev/null
+++ b/arch/metag/kernel/machines.c
@@ -0,0 +1,21 @@
+/*
+ *  arch/metag/kernel/machines.c
+ *
+ *  Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ *  Generic Meta Boards.
+ */
+
+#include <linux/init.h>
+#include <asm/irq.h>
+#include <asm/mach/arch.h>
+
+static const char *meta_boards_compat[] __initdata = {
+	"img,meta",
+	NULL,
+};
+
+MACHINE_START(META, "Generic Meta")
+	.dt_compat	= meta_boards_compat,
+	.init_irq	= init_soc_IRQ,
+MACHINE_END
diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c
new file mode 100644
index 0000000..41d6ad9
--- /dev/null
+++ b/arch/metag/kernel/setup.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2005-2012 Imagination Technologies Ltd.
+ *
+ * This file contains the architecture-dependant parts of system setup.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/console.h>
+#include <linux/genhd.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/root_dev.h>
+#include <linux/initrd.h>
+#include <linux/seq_file.h>
+#include <linux/pfn.h>
+#include <linux/start_kernel.h>
+#include <linux/cpu.h>
+#include <linux/memblock.h>
+#include <linux/of_fdt.h>
+
+#include <asm/cachepart.h>
+#include <asm/clock.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/processor.h>
+#include <asm/traps.h>
+#include <asm/mmu.h>
+#include <asm/cpu.h>
+#include <asm/hwthread.h>
+#include <asm/mmzone.h>
+#include <asm/l2cache.h>
+#include <asm/prom.h>
+#include <asm/mach/arch.h>
+#include <asm/core_reg.h>
+#include <asm/highmem.h>
+
+/* PRIV protect as many registers as possible. */
+#define DEFAULT_PRIV	0xff0f7f00
+
+/* Enable unaligned access checking. */
+#define UNALIGNED_PRIV	0x00000010
+
+#ifdef CONFIG_META21_UNALIGNED
+#define PRIV_BITS (DEFAULT_PRIV | UNALIGNED_PRIV)
+#else
+#define PRIV_BITS DEFAULT_PRIV
+#endif
+
+extern char _heap_start[];
+
+#ifdef CONFIG_METAG_BUILTIN_DTB
+extern u32 __dtb_start[];
+#endif
+
+struct machine_desc *machine_desc __initdata;
+
+/*
+ * Map a Linux CPU number to a hardware thread ID
+ * In SMP this will be setup with the correct mapping at startup; in UP this
+ * will map to the HW thread on which we are running.
+ */
+u8 cpu_2_hwthread_id[NR_CPUS] __read_mostly = {
+	[0 ... NR_CPUS-1] = BAD_HWTHREAD_ID
+};
+
+/*
+ * Map a hardware thread ID to a Linux CPU number
+ * In SMP this will be fleshed out with the correct CPU ID for a particular
+ * hardware thread. In UP this will be initialised with the boot CPU ID.
+ */
+u8 hwthread_id_2_cpu[4] __read_mostly = {
+	[0 ... 3] = BAD_CPU_ID
+};
+
+/* The relative offset of the MMU mapped memory (from ldlk or bootloader)
+ * to the real physical memory.  This is needed as we have to use the
+ * physical addresses in the MMU tables (pte entries), and not the virtual
+ * addresses.
+ * This variable is used in the __pa() and __va() macros, and should
+ * probably only be used via them.
+ */
+unsigned int meta_memoffset;
+
+static char __initdata *original_cmd_line;
+#ifdef CONFIG_METAG_CMDLINE_BOOL
+static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] =
+					CONFIG_METAG_CMDLINE;
+#endif
+
+DEFINE_PER_CPU(PTBI, pTBI);
+
+/*
+ * Mapping are specified as "CPU_ID:HWTHREAD_ID", e.g.
+ *
+ *	"hwthread_map=0:1,1:2,2:3,3:0"
+ *
+ *	Linux CPU ID	HWTHREAD_ID
+ *	---------------------------
+ *	    0		      1
+ *	    1		      2
+ *	    2		      3
+ *	    3		      0
+ */
+static int __init parse_hwthread_map(char *p)
+{
+	int cpu;
+
+	while (*p) {
+		cpu = (*p++) - '0';
+		if (cpu < 0 || cpu > 9)
+			goto err_cpu;
+
+		p++;		/* skip semi-colon */
+		cpu_2_hwthread_id[cpu] = (*p++) - '0';
+		if (cpu_2_hwthread_id[cpu] >= 4)
+			goto err_thread;
+		hwthread_id_2_cpu[cpu_2_hwthread_id[cpu]] = cpu;
+
+		if (*p == ',')
+			p++;		/* skip comma */
+	}
+
+	return 0;
+err_cpu:
+	pr_err("%s: hwthread_map cpu argument out of range\n", __func__);
+	return -EINVAL;
+err_thread:
+	pr_err("%s: hwthread_map thread argument out of range\n", __func__);
+	return -EINVAL;
+}
+early_param("hwthread_map", parse_hwthread_map);
+
+void __init dump_machine_table(void)
+{
+	struct machine_desc *p;
+	const char **compat;
+
+	pr_info("Available machine support:\n\tNAME\t\tCOMPATIBLE LIST\n");
+	for_each_machine_desc(p) {
+		pr_info("\t%s\t[", p->name);
+		for (compat = p->dt_compat; compat && *compat; ++compat)
+			printk(" '%s'", *compat);
+		printk(" ]\n");
+	}
+
+	pr_info("\nPlease check your kernel config and/or bootloader.\n");
+
+	hard_processor_halt(HALT_PANIC);
+}
+
+#ifdef CONFIG_META_HALT_ON_PANIC
+static int metag_panic_event(struct notifier_block *this, unsigned long event,
+			     void *ptr)
+{
+	hard_processor_halt(HALT_PANIC);
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block metag_panic_block = {
+	metag_panic_event,
+	NULL,
+	0
+};
+#endif
+
+void __init setup_arch(char **cmdline_p)
+{
+	unsigned long start_pfn;
+	unsigned long text_start = (unsigned long)(&_stext);
+	unsigned long cpu = smp_processor_id();
+	unsigned long heap_start, heap_end;
+	unsigned long start_pte;
+	PTBI _pTBI;
+	PTBISEG p_heap;
+	int heap_id, i;
+
+	/* try interpreting the argument as a device tree */
+	machine_desc = setup_machine_fdt(original_cmd_line);
+	/* if it doesn't look like a device tree it must be a command line */
+	if (!machine_desc) {
+#ifdef CONFIG_METAG_BUILTIN_DTB
+		/* try the embedded device tree */
+		machine_desc = setup_machine_fdt(__dtb_start);
+		if (!machine_desc)
+			panic("Invalid embedded device tree.");
+#else
+		/* use the default machine description */
+		machine_desc = default_machine_desc();
+#endif
+		/* append the bootloader cmdline to any builtin fdt cmdline */
+		if (boot_command_line[0] && original_cmd_line[0])
+			strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+		strlcat(boot_command_line, original_cmd_line,
+			COMMAND_LINE_SIZE);
+	}
+	setup_meta_clocks(machine_desc->clocks);
+
+#ifdef CONFIG_METAG_CMDLINE_BOOL
+#ifdef CONFIG_METAG_CMDLINE_OVERRIDE
+	strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
+#else
+	if (builtin_cmdline[0] && boot_command_line[0]) {
+		/* prepend builtin cmdline */
+		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
+
+	*cmdline_p = boot_command_line;
+	parse_early_param();
+
+	/*
+	 * Make sure we don't alias in dcache or icache
+	 */
+	check_for_cache_aliasing(cpu);
+
+
+#ifdef CONFIG_META_HALT_ON_PANIC
+	atomic_notifier_chain_register(&panic_notifier_list,
+				       &metag_panic_block);
+#endif
+
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp = &dummy_con;
+#endif
+
+	if (!(TBI_GETREG(TXSTATUS) & 0x20000))
+		panic("Privilege must be enabled for this thread.");
+
+	_pTBI = __TBI(TBID_ISTAT_BIT);
+
+	per_cpu(pTBI, cpu) = _pTBI;
+
+	if (!per_cpu(pTBI, cpu))
+		panic("No TBI found!");
+
+	/*
+	 * Initialize all interrupt vectors to our copy of __TBIUnExpXXX,
+	 * rather than the version from the bootloader. This makes call
+	 * stacks easier to understand and may allow us to unmap the
+	 * bootloader at some point.
+	 *
+	 * We need to keep the LWK handler that TBI installed in order to
+	 * be able to do inter-thread comms.
+	 */
+	for (i = 0; i <= TBID_SIGNUM_MAX; i++)
+		if (i != TBID_SIGNUM_LWK)
+			_pTBI->fnSigs[i] = __TBIUnExpXXX;
+
+	/* A Meta requirement is that the kernel is loaded (virtually)
+	 * at the PAGE_OFFSET.
+	 */
+	if (PAGE_OFFSET != text_start)
+		panic("Kernel not loaded at PAGE_OFFSET (%#x) but at %#lx.",
+		      PAGE_OFFSET, text_start);
+
+	start_pte = mmu_read_second_level_page(text_start);
+
+	/*
+	 * Kernel pages should have the PRIV bit set by the bootloader.
+	 */
+	if (!(start_pte & _PAGE_KERNEL))
+		panic("kernel pte does not have PRIV set");
+
+	/*
+	 * See __pa and __va in include/asm/page.h.
+	 * This value is negative when running in local space but the
+	 * calculations work anyway.
+	 */
+	meta_memoffset = text_start - (start_pte & PAGE_MASK);
+
+	/* Now lets look at the heap space */
+	heap_id = (__TBIThreadId() & TBID_THREAD_BITS)
+		+ TBID_SEG(0, TBID_SEGSCOPE_LOCAL, TBID_SEGTYPE_HEAP);
+
+	p_heap = __TBIFindSeg(NULL, heap_id);
+
+	if (!p_heap)
+		panic("Could not find heap from TBI!");
+
+	/* The heap begins at the first full page after the kernel data. */
+	heap_start = (unsigned long) &_heap_start;
+
+	/* The heap ends at the end of the heap segment specified with
+	 * ldlk.
+	 */
+	if (is_global_space(text_start)) {
+		pr_debug("WARNING: running in global space!\n");
+		heap_end = (unsigned long)p_heap->pGAddr + p_heap->Bytes;
+	} else {
+		heap_end = (unsigned long)p_heap->pLAddr + p_heap->Bytes;
+	}
+
+	ROOT_DEV = Root_RAM0;
+
+	/* init_mm is the mm struct used for the first task.  It is then
+	 * cloned for all other tasks spawned from that task.
+	 *
+	 * Note - we are using the virtual addresses here.
+	 */
+	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)heap_start;
+
+	min_low_pfn = PFN_UP(__pa(text_start));
+	max_low_pfn = PFN_DOWN(__pa(heap_end));
+
+	pfn_base = min_low_pfn;
+
+	/* Round max_pfn up to a 4Mb boundary. The free_bootmem_node()
+	 * call later makes sure to keep the rounded up pages marked reserved.
+	 */
+	max_pfn = max_low_pfn + ((1 << MAX_ORDER) - 1);
+	max_pfn &= ~((1 << MAX_ORDER) - 1);
+
+	start_pfn = PFN_UP(__pa(heap_start));
+
+	if (min_low_pfn & ((1 << MAX_ORDER) - 1)) {
+		/* Theoretically, we could expand the space that the
+		 * bootmem allocator covers - much as we do for the
+		 * 'high' address, and then tell the bootmem system
+		 * that the lowest chunk is 'not available'.  Right
+		 * now it is just much easier to constrain the
+		 * user to always MAX_ORDER align their kernel space.
+		 */
+
+		panic("Kernel must be %d byte aligned, currently at %#lx.",
+		      1 << (MAX_ORDER + PAGE_SHIFT),
+		      min_low_pfn << PAGE_SHIFT);
+	}
+
+#ifdef CONFIG_HIGHMEM
+	highstart_pfn = highend_pfn = max_pfn;
+	high_memory = (void *) __va(PFN_PHYS(highstart_pfn));
+#else
+	high_memory = (void *)__va(PFN_PHYS(max_pfn));
+#endif
+
+	paging_init(heap_end);
+
+	setup_txprivext();
+
+	/* Setup the boot cpu's mapping. The rest will be setup below. */
+	cpu_2_hwthread_id[smp_processor_id()] = hard_processor_id();
+	hwthread_id_2_cpu[hard_processor_id()] = smp_processor_id();
+
+	unflatten_device_tree();
+
+#ifdef CONFIG_SMP
+	smp_init_cpus();
+#endif
+
+	if (machine_desc->init_early)
+		machine_desc->init_early();
+}
+
+static int __init customize_machine(void)
+{
+	/* customizes platform devices, or adds new ones */
+	if (machine_desc->init_machine)
+		machine_desc->init_machine();
+	return 0;
+}
+arch_initcall(customize_machine);
+
+static int __init init_machine_late(void)
+{
+	if (machine_desc->init_late)
+		machine_desc->init_late();
+	return 0;
+}
+late_initcall(init_machine_late);
+
+static const char *get_cpu_capabilities(unsigned int txenable)
+{
+#ifdef CONFIG_META21
+	/* See CORE_ID in META HTP.GP TRM - Architecture Overview 2.1.238 */
+	int coreid = readl(METAC_CORE_ID);
+	unsigned int dsp_type = (coreid >> 3) & 7;
+	unsigned int fpu_type = (coreid >> 7) & 3;
+
+	switch (dsp_type | fpu_type << 3) {
+	case (0x00): return "EDSP";
+	case (0x01): return "DSP";
+	case (0x08): return "EDSP+LFPU";
+	case (0x09): return "DSP+LFPU";
+	case (0x10): return "EDSP+FPU";
+	case (0x11): return "DSP+FPU";
+	}
+	return "UNKNOWN";
+
+#else
+	if (!(txenable & TXENABLE_CLASS_BITS))
+		return "DSP";
+	else
+		return "";
+#endif
+}
+
+/*
+ *	Get CPU information for use by the procfs.
+ */
+#ifdef CONFIG_PROC_FS
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+	const char *cpu;
+	unsigned int txenable, thread_id, major, minor;
+	unsigned long clockfreq = get_coreclock();
+#ifdef CONFIG_SMP
+	int i;
+	unsigned long lpj;
+#endif
+
+	cpu = "META";
+
+	asm __volatile__("MOV %0,TXENABLE\n" : "=r" (txenable));
+	major = (txenable & TXENABLE_MAJOR_REV_BITS) >> TXENABLE_MAJOR_REV_S;
+	minor = (txenable & TXENABLE_MINOR_REV_BITS) >> TXENABLE_MINOR_REV_S;
+	thread_id = (txenable >> 8) & 0x3;
+
+
+#ifdef CONFIG_SMP
+	for_each_online_cpu(i) {
+		lpj = per_cpu(cpu_data, i).loops_per_jiffy;
+		txenable = core_reg_read(TXUCT_ID, TXENABLE_REGNUM,
+							cpu_2_hwthread_id[i]);
+
+		seq_printf(m, "CPU:\t\t%s %d.%d (thread %d)\n"
+			      "Clocking:\t%lu.%1luMHz\n"
+			      "BogoMips:\t%lu.%02lu\n"
+			      "Calibration:\t%lu loops\n"
+			      "Capabilities:\t%s\n\n",
+			      cpu, major, minor, i,
+			      clockfreq / 1000000, (clockfreq / 100000) % 10,
+			      lpj / (500000 / HZ), (lpj / (5000 / HZ)) % 100,
+			      lpj,
+			      get_cpu_capabilities(txenable));
+	}
+#else
+	seq_printf(m, "CPU:\t\t%s %d.%d (thread %d)\n"
+		   "Clocking:\t%lu.%1luMHz\n"
+		   "BogoMips:\t%lu.%02lu\n"
+		   "Calibration:\t%lu loops\n"
+		   "Capabilities:\t%s\n",
+		   cpu, major, minor, thread_id,
+		   clockfreq / 1000000, (clockfreq / 100000) % 10,
+		   loops_per_jiffy / (500000 / HZ),
+		   (loops_per_jiffy / (5000 / HZ)) % 100,
+		   loops_per_jiffy,
+		   get_cpu_capabilities(txenable));
+#endif /* CONFIG_SMP */
+
+#ifdef CONFIG_META_L2C
+	if (meta_l2c_is_present()) {
+		seq_printf(m, "L2 cache:\t%s\n"
+			      "L2 cache size:\t%d KB\n",
+			      meta_l2c_is_enabled() ? "enabled" : "disabled",
+			      meta_l2c_size() >> 10);
+	}
+#endif
+	return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	return (void *)(*pos == 0);
+}
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	return NULL;
+}
+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,
+};
+#endif /* CONFIG_PROC_FS */
+
+void __init metag_start_kernel(char *args)
+{
+	/* Zero the timer register so timestamps are from the point at
+	 * which the kernel started running.
+	 */
+	TBI_SETREG(TXTIMER, 0);
+
+	/* Clear the bss. */
+	memset(__bss_start, 0,
+	       (unsigned long)__bss_stop - (unsigned long)__bss_start);
+
+	/* Remember where these are for use in setup_arch */
+	original_cmd_line = args;
+
+	current_thread_info()->cpu = hard_processor_id();
+
+	start_kernel();
+}
+
+/*
+ * Setup TXPRIVEXT register to be prevent userland from touching our
+ * precious registers.
+ */
+void setup_txprivext(void)
+{
+	TBI_SETREG(TXPRIVEXT, PRIV_BITS);
+}
+
+PTBI pTBI_get(unsigned int cpu)
+{
+	return per_cpu(pTBI, cpu);
+}
+
+#if defined(CONFIG_META_DSP) && defined(CONFIG_META_FPU)
+char capabilites[] = "dsp fpu";
+#elif defined(CONFIG_META_DSP)
+char capabilites[] = "dsp";
+#elif defined(CONFIG_META_FPU)
+char capabilites[] = "fpu";
+#else
+char capabilites[] = "";
+#endif
+
+static struct ctl_table caps_kern_table[] = {
+	{
+		.procname	= "capabilities",
+		.data		= capabilites,
+		.maxlen		= sizeof(capabilites),
+		.mode		= 0444,
+		.proc_handler	= proc_dostring,
+	},
+	{}
+};
+
+static struct ctl_table caps_root_table[] = {
+	{
+		.procname	= "kernel",
+		.mode		= 0555,
+		.child		= caps_kern_table,
+	},
+	{}
+};
+
+static int __init capabilities_register_sysctl(void)
+{
+	struct ctl_table_header *caps_table_header;
+
+	caps_table_header = register_sysctl_table(caps_root_table);
+	if (!caps_table_header) {
+		pr_err("Unable to register CAPABILITIES sysctl\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+core_initcall(capabilities_register_sysctl);
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 11/40] metag: Signal handling
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (7 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 08/40] metag: Boot James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 12/40] metag: Build infrastructure James Hogan
                   ` (31 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add signal handling code for metag.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/sigcontext.h |   23 +++
 arch/metag/include/asm/siginfo.h    |    8 +
 arch/metag/kernel/signal.c          |  340 +++++++++++++++++++++++++++++++++++
 3 files changed, 371 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/sigcontext.h
 create mode 100644 arch/metag/include/asm/siginfo.h
 create mode 100644 arch/metag/kernel/signal.c

diff --git a/arch/metag/include/asm/sigcontext.h b/arch/metag/include/asm/sigcontext.h
new file mode 100644
index 0000000..292550b
--- /dev/null
+++ b/arch/metag/include/asm/sigcontext.h
@@ -0,0 +1,23 @@
+#ifndef _ASM_METAG_SIGCONTEXT_H
+#define _ASM_METAG_SIGCONTEXT_H
+
+#define METAG_ALL_VALUES
+#define METAC_ALL_VALUES
+#include <asm/tbx/metagtbx.h>
+/* In a sigcontext structure we need to store the active state of the
+ * user process so that it does not get trashed when we call the signal
+ * handler. That not really the same as a user context that we are
+ * going to store on syscall etc.
+ */
+struct sigcontext {
+	TBICTX ctx;
+
+	/* Space to save catch buffers. To tell if the catch buffers have been
+	 * saved then check the CBF bit in the ctx flags.  See TBX headers for
+	 * more info.
+	 */
+	TBICTXEXTCB0 extcb0[5];
+	unsigned long oldmask;
+};
+
+#endif
diff --git a/arch/metag/include/asm/siginfo.h b/arch/metag/include/asm/siginfo.h
new file mode 100644
index 0000000..b2e0c8b
--- /dev/null
+++ b/arch/metag/include/asm/siginfo.h
@@ -0,0 +1,8 @@
+#ifndef _METAG_SIGINFO_H
+#define _METAG_SIGINFO_H
+
+#define __ARCH_SI_TRAPNO
+
+#include <asm-generic/siginfo.h>
+
+#endif
diff --git a/arch/metag/kernel/signal.c b/arch/metag/kernel/signal.c
new file mode 100644
index 0000000..e8146f6
--- /dev/null
+++ b/arch/metag/kernel/signal.c
@@ -0,0 +1,340 @@
+/*
+ *  Copyright (C) 1991,1992  Linus Torvalds
+ *  Copyright (C) 2005,2006,2007,2008,2009  Imagination Technologies Ltd.
+ *
+ *  1997-11-28  Modified for POSIX.1b signals by Richard Henderson
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/personality.h>
+#include <linux/uaccess.h>
+#include <linux/tracehook.h>
+
+#include <asm/ucontext.h>
+#include <asm/cacheflush.h>
+#include <asm/switch.h>
+#include <asm/syscall.h>
+#include <asm/syscalls.h>
+
+#define REG_FLAGS	ctx.SaveMask
+#define REG_RETVAL	ctx.DX[0].U0
+#define REG_SYSCALL	ctx.DX[0].U1
+#define REG_SP		ctx.AX[0].U0
+#define REG_ARG1	ctx.DX[3].U1
+#define REG_ARG2	ctx.DX[3].U0
+#define REG_ARG3	ctx.DX[2].U1
+#define REG_PC		ctx.CurrPC
+#define REG_RTP		ctx.DX[4].U1
+
+int sys_sigaltstack(const stack_t __user *uss,
+		    stack_t __user *uoss,
+		    unsigned long arg3,
+		    unsigned long arg4,
+		    unsigned long arg5,
+		    unsigned long arg6,
+		    struct pt_regs *regs)
+{
+	return do_sigaltstack(uss, uoss, regs->REG_SP);
+}
+
+struct rt_sigframe {
+	struct siginfo info;
+	struct ucontext uc;
+	unsigned long retcode[2];
+};
+
+static int restore_sigcontext(struct pt_regs *regs,
+			      struct sigcontext __user *sc)
+{
+	int err;
+
+	/* Always make any pending restarted system calls return -EINTR */
+	current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+	err = __copy_from_user(&regs->ctx, sc, sizeof(regs->ctx));
+
+	if (regs->REG_FLAGS & TBICTX_XCBF_BIT) {
+		err |= __copy_from_user(regs->extcb0,
+					(void __user *)((unsigned long)sc +
+							sizeof(regs->ctx)),
+					sizeof(regs->extcb0));
+	}
+
+	/* This is a user-mode context. */
+	regs->REG_FLAGS |= TBICTX_PRIV_BIT;
+
+	return err;
+}
+
+int sys_rt_sigreturn(unsigned long arg1,
+		     unsigned long arg2,
+		     unsigned long arg3,
+		     unsigned long arg4,
+		     unsigned long arg5,
+		     unsigned long arg6,
+		     struct pt_regs *regs)    /* regs always arg7 on Meta */
+{
+	/* NOTE - Meta stack goes UPWARDS - so we wind the stack back */
+	struct rt_sigframe __user *frame;
+	sigset_t set;
+	stack_t st;
+
+	frame = (__force struct rt_sigframe __user *)(regs->REG_SP -
+						      sizeof(*frame));
+
+	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+		goto badframe;
+
+	if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+		goto badframe;
+
+	set_current_blocked(&set);
+
+	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+		goto badframe;
+
+	if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
+		goto badframe;
+	/* It is more difficult to avoid calling this function than to
+	   call it and ignore errors.  */
+	do_sigaltstack((__force const stack_t __user *)&st, NULL, regs->REG_SP);
+
+	return regs->REG_RETVAL;
+
+badframe:
+	force_sig(SIGSEGV, current);
+
+	return 0;
+}
+
+static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
+			    unsigned long mask)
+{
+	int err;
+
+	err = __copy_to_user(sc, &regs->ctx, sizeof(regs->ctx));
+
+	if (regs->REG_FLAGS & TBICTX_XCBF_BIT) {
+		err |= __copy_to_user((void __user *)((unsigned long)sc +
+						      sizeof(regs->ctx)),
+				      regs->extcb0,
+				      sizeof(regs->extcb0));
+	}
+
+	/* OK, clear that cbuf flag in the old context, or our stored
+	 * catch buffer will be restored when we go to call the signal
+	 * handler. Also clear out the CBRP RA/RD pipe bit incase
+	 * that is pending as well!
+	 * Note that as we have already stored this context, these
+	 * flags will get restored on sigreturn to their original
+	 * state.
+	 */
+	regs->REG_FLAGS &= ~(TBICTX_XCBF_BIT | TBICTX_CBUF_BIT |
+			     TBICTX_CBRP_BIT);
+
+	/* Clear out the LSM_STEP bits in case we are in the middle of
+	 * and MSET/MGET.
+	 */
+	regs->ctx.Flags &= ~TXSTATUS_LSM_STEP_BITS;
+
+	err |= __put_user(mask, &sc->oldmask);
+
+	return err;
+}
+
+/*
+ * Determine which stack to use..
+ */
+static void __user *get_sigframe(struct k_sigaction *ka, unsigned long sp,
+				 size_t frame_size)
+{
+	/* Meta stacks grows upwards */
+	if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
+		sp = current->sas_ss_sp;
+
+	sp = (sp + 7) & ~7;			/* 8byte align stack */
+
+	return (void __user *)sp;
+}
+
+static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+			  sigset_t *set, struct pt_regs *regs)
+{
+	struct rt_sigframe __user *frame;
+	int err = -EFAULT;
+	unsigned long code;
+
+	frame = get_sigframe(ka, regs->REG_SP, sizeof(*frame));
+	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+		goto out;
+
+	err = copy_siginfo_to_user(&frame->info, info);
+
+	/* Create the ucontext.  */
+	err |= __put_user(0, &frame->uc.uc_flags);
+	err |= __put_user(0, (unsigned long __user *)&frame->uc.uc_link);
+	err |= __put_user(current->sas_ss_sp,
+			  (unsigned long __user *)&frame->uc.uc_stack.ss_sp);
+	err |= __put_user(sas_ss_flags(regs->REG_SP),
+			  &frame->uc.uc_stack.ss_flags);
+	err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+	err |= setup_sigcontext(&frame->uc.uc_mcontext,
+				regs, set->sig[0]);
+	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+	if (err)
+		goto out;
+
+	/* Set up to return from userspace.  */
+
+	/* MOV D1Re0 (D1.0), #__NR_rt_sigreturn */
+	code = 0x03000004 | (__NR_rt_sigreturn << 3);
+	err |= __put_user(code, (unsigned long __user *)(&frame->retcode[0]));
+
+	/* SWITCH #__METAG_SW_SYS */
+	code = __METAG_SW_ENCODING(SYS);
+	err |= __put_user(code, (unsigned long __user *)(&frame->retcode[1]));
+
+	if (err)
+		goto out;
+
+	/* Set up registers for signal handler */
+	regs->REG_RTP = (unsigned long) frame->retcode;
+	regs->REG_SP = (unsigned long) frame + sizeof(*frame);
+	regs->REG_ARG1 = sig;
+	regs->REG_ARG2 = (unsigned long) &frame->info;
+	regs->REG_ARG3 = (unsigned long) &frame->uc;
+	regs->REG_PC = (unsigned long) ka->sa.sa_handler;
+
+	pr_debug("SIG deliver (%s:%d): sp=%p pc=%08x pr=%08x\n",
+		 current->comm, current->pid, frame, regs->REG_PC,
+		 regs->REG_RTP);
+
+	/* Now pass size of 'new code' into sigtramp so we can do a more
+	 * effective cache flush - directed rather than 'full flush'.
+	 */
+	flush_cache_sigtramp(regs->REG_RTP, sizeof(frame->retcode));
+out:
+	if (err) {
+		force_sigsegv(sig, current);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static void handle_signal(unsigned long sig, siginfo_t *info,
+			  struct k_sigaction *ka, struct pt_regs *regs,
+			  int from_syscall, unsigned int orig_syscall)
+{
+	sigset_t *oldset = sigmask_to_save();
+
+	/* Are we from a system call? */
+	if (from_syscall) {
+		/* If so, check system call restarting.. */
+		switch (syscall_get_error(current, regs)) {
+		case -ERESTART_RESTARTBLOCK:
+		case -ERESTARTNOHAND:
+			regs->REG_RETVAL = -EINTR;
+			break;
+
+		case -ERESTARTSYS:
+			if (!(ka->sa.sa_flags & SA_RESTART)) {
+				regs->REG_RETVAL = -EINTR;
+				break;
+			}
+		/* fallthrough */
+		case -ERESTARTNOINTR:
+			regs->REG_SYSCALL = orig_syscall;
+			regs->REG_PC -= 4;
+			break;
+		}
+	}
+
+	/* Set up the stack frame */
+	if (setup_rt_frame(sig, ka, info, oldset, regs))
+		return;
+
+	signal_delivered(sig, info, ka, regs, test_thread_flag(TIF_SINGLESTEP));
+}
+
+ /* Notes for Meta.
+  * We have moved from the old 2.4.9 SH way of using syscall_nr (in the stored
+  * context) to passing in the syscall flag and orig_syscall on the stack.
+  * This is for two reasons:
+  * 1) having syscall_nr in our context does not fit with TBI, and corrupted
+  *    the stack.
+  * 2) We need to have orig_syscall and from_syscall to implement syscall
+  *    restarting.
+  */
+static void do_signal(struct pt_regs *regs, int from_syscall,
+		      unsigned int orig_syscall)
+{
+	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;
+
+	signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+	if (signr > 0) {
+		/* Whee! Actually deliver the signal.  */
+		handle_signal(signr, &info, &ka, regs, from_syscall,
+			      orig_syscall);
+		return;
+	}
+
+	/* Did we come from a system call? */
+	if (from_syscall) {
+		/* Restart the system call - no handlers present */
+		switch (syscall_get_error(current, regs)) {
+		case -ERESTARTNOHAND:
+		case -ERESTARTSYS:
+		case -ERESTARTNOINTR:
+			regs->REG_SYSCALL = orig_syscall;
+			regs->REG_PC -= 4;
+			break;
+
+		case -ERESTART_RESTARTBLOCK:
+			regs->REG_SYSCALL = __NR_restart_syscall;
+			regs->REG_PC -= 4;
+			break;
+		}
+	}
+
+	/*
+	 * If there's no signal to deliver, we just put the saved sigmask
+	 * back.
+	 */
+	restore_saved_sigmask();
+}
+
+void do_notify_resume(struct pt_regs *regs, int from_syscall,
+		      unsigned int orig_syscall,
+		      unsigned long thread_info_flags)
+{
+	/* deal with pending signal delivery */
+	if (thread_info_flags & _TIF_SIGPENDING)
+		do_signal(regs, from_syscall, orig_syscall);
+
+	if (thread_info_flags & _TIF_NOTIFY_RESUME) {
+		clear_thread_flag(TIF_NOTIFY_RESUME);
+		tracehook_notify_resume(regs);
+	}
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (8 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 11/40] metag: Signal handling James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 19:35   ` Sam Ravnborg
  2012-11-09 14:46   ` Arnd Bergmann
  2012-10-31 16:13 ` [RFC PATCH v1 13/40] metag: Device tree James Hogan
                   ` (30 subsequent siblings)
  40 siblings, 2 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add metag build infrastructure.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/Kconfig                     |  292 ++++++++++++++++++++++++++++++++
 arch/metag/Kconfig.debug               |   40 +++++
 arch/metag/Kconfig.soc                 |   58 +++++++
 arch/metag/Makefile                    |   90 ++++++++++
 arch/metag/boot/Makefile               |   72 ++++++++
 arch/metag/configs/meta1_defconfig     |   42 +++++
 arch/metag/configs/meta2_defconfig     |   42 +++++
 arch/metag/configs/meta2_smp_defconfig |   43 +++++
 arch/metag/include/asm/Kbuild          |   59 +++++++
 arch/metag/include/asm/tbx/Kbuild      |    8 +
 arch/metag/kernel/.gitignore           |    1 +
 arch/metag/kernel/Makefile             |   37 ++++
 arch/metag/kernel/asm-offsets.c        |   14 ++
 arch/metag/kernel/metag_ksyms.c        |   77 +++++++++
 arch/metag/kernel/vmlinux.lds.S        |   71 ++++++++
 arch/metag/lib/Makefile                |    7 +
 arch/metag/mm/Kconfig                  |  153 +++++++++++++++++
 arch/metag/mm/Makefile                 |   17 ++
 arch/metag/tbx/Makefile                |   17 ++
 lib/Kconfig.debug                      |    6 +-
 20 files changed, 1143 insertions(+), 3 deletions(-)
 create mode 100644 arch/metag/Kconfig
 create mode 100644 arch/metag/Kconfig.debug
 create mode 100644 arch/metag/Kconfig.soc
 create mode 100644 arch/metag/Makefile
 create mode 100644 arch/metag/boot/Makefile
 create mode 100644 arch/metag/configs/meta1_defconfig
 create mode 100644 arch/metag/configs/meta2_defconfig
 create mode 100644 arch/metag/configs/meta2_smp_defconfig
 create mode 100644 arch/metag/include/asm/Kbuild
 create mode 100644 arch/metag/include/asm/tbx/Kbuild
 create mode 100644 arch/metag/kernel/.gitignore
 create mode 100644 arch/metag/kernel/Makefile
 create mode 100644 arch/metag/kernel/asm-offsets.c
 create mode 100644 arch/metag/kernel/metag_ksyms.c
 create mode 100644 arch/metag/kernel/vmlinux.lds.S
 create mode 100644 arch/metag/lib/Makefile
 create mode 100644 arch/metag/mm/Kconfig
 create mode 100644 arch/metag/mm/Makefile
 create mode 100644 arch/metag/tbx/Makefile

diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
new file mode 100644
index 0000000..3a19cb6
--- /dev/null
+++ b/arch/metag/Kconfig
@@ -0,0 +1,292 @@
+config SYMBOL_PREFIX
+	string
+	default "_"
+
+config METAG
+	def_bool y
+	select EMBEDDED
+	select HAVE_64BIT_ALIGNED_STRUCT
+	select HAVE_MEMBLOCK
+	select HAVE_MEMBLOCK_NODE_MAP
+	select HAVE_ARCH_TRACEHOOK
+	select HAVE_IRQ_WORK
+	select HAVE_KERNEL_GZIP
+	select HAVE_KERNEL_BZIP2
+	select HAVE_KERNEL_LZMA
+	select HAVE_KERNEL_XZ
+	select HAVE_KERNEL_LZO
+	select HAVE_SYSCALL_TRACEPOINTS
+	select HAVE_GENERIC_HARDIRQS
+	select GENERIC_ATOMIC64
+	select GENERIC_CLOCKEVENTS
+	select GENERIC_IRQ_SHOW
+	select GENERIC_SMP_IDLE_THREAD
+	select IRQ_DOMAIN
+	select OF
+	select OF_EARLY_FLATTREE
+	help
+	  Imagination Technologies Meta processors are a family of multi-
+	  threaded DSPs designed for embedded systems.
+
+config STACKTRACE_SUPPORT
+	def_bool y
+
+config LOCKDEP_SUPPORT
+	def_bool y
+
+config HAVE_LATENCYTOP_SUPPORT
+	def_bool y
+
+config RWSEM_GENERIC_SPINLOCK
+	def_bool y
+
+config RWSEM_XCHGADD_ALGORITHM
+	bool
+
+config GENERIC_HWEIGHT
+	def_bool y
+
+config IRQ_PER_CPU
+	def_bool y
+
+config GENERIC_CALIBRATE_DELAY
+	def_bool y
+
+config GENERIC_GPIO
+	def_bool n
+
+config NO_IOPORT
+	def_bool y
+
+source "init/Kconfig"
+
+source "kernel/Kconfig.freezer"
+
+menu "Processor type and features"
+
+config MMU
+	def_bool y
+
+config STACK_GROWSUP
+	def_bool y
+
+config HOTPLUG_CPU
+	bool "Enable CPU hotplug support"
+	depends on SMP && HOTPLUG
+	help
+	  Say Y here to allow turning CPUs off and on. CPUs can be
+	  controlled through /sys/devices/system/cpu.
+
+	  Say N if you want to disable CPU hotplug.
+
+config HIGHMEM
+	bool "High Memory Support"
+	depends on MMU
+	help
+	  The address space of Meta processors is only 4 Gigabytes large
+	  and it has to accommodate user address space, kernel address
+	  space as well as some memory mapped IO. That means that, if you
+	  have a large amount of physical memory and/or IO, not all of the
+	  memory can be "permanently mapped" by the kernel. The physical
+	  memory that is not permanently mapped is called "high memory".
+
+	  Depending on the selected kernel/user memory split, minimum
+	  vmalloc space and actual amount of RAM, you may not need this
+	  option which should result in a slightly faster kernel.
+
+	  If unsure, say n.
+
+source "arch/metag/mm/Kconfig"
+
+source "arch/metag/Kconfig.soc"
+
+config META12
+	bool
+	---help---
+	  Select this from the SoC config symbol to indicate that it contains a
+	  Meta 1.2 core.
+
+config META21
+	bool
+	---help---
+	  Select this from the SoC config symbol to indicate that it contains a
+	  Meta 2.1 core.
+
+config SMP
+	bool "Symmetric multi-processing support"
+	depends on META21
+	select USE_GENERIC_SMP_HELPERS
+	---help---
+	  This enables support for systems with more than one thread running
+	  Linux. If you have a system with only one thread running Linux,
+	  say N. Otherwise, say Y.
+
+config NR_CPUS
+	int "Maximum number of CPUs (2-4)" if SMP
+	range 2 4 if SMP
+	default "1" if !SMP
+	default "4" if SMP
+
+config SMP_WRITE_REORDERING
+	bool
+	---help---
+	  This attempts to prevent cache-memory incoherence due to external
+	  reordering of writes from different hardware threads when SMP is
+	  enabled. It adds fences (system event 0) to smp_mb and smp_rmb in an
+	  attempt to catch some of the cases, and also before writes to shared
+	  memory in LOCK1 protected atomics and spinlocks.
+	  This will not completely prevent cache incoherency on affected cores.
+
+config LNKGET_AROUND_CACHE
+	bool
+	depends on META21
+	---help---
+	  This indicates that the LNKGET/LNKSET instructions go around the
+	  cache, which requires some extra cache flushes when the memory needs
+	  to be accessed by normal GET/SET instructions too.
+
+choice
+	prompt "Atomicity primitive"
+	default ATOMICITY_LNKGET
+	---help---
+	  This option selects the mechanism for performing atomic operations.
+
+config ATOMICITY_IRQSOFF
+	depends on !SMP
+	bool "irqsoff"
+	---help---
+	  This option disables interrupts to achieve atomicity. This mechanism
+	  is not SMP-safe.
+
+config ATOMICITY_LNKGET
+	depends on META21
+	bool "lnkget/lnkset"
+	---help---
+	  This option uses the LNKGET and LNKSET instructions to achieve
+	  atomicity. LNKGET/LNKSET are load-link/store-conditional instructions.
+	  Choose this option if your system requires low latency.
+
+config ATOMICITY_LOCK1
+	depends on SMP
+	bool "lock1"
+	---help---
+	  This option uses the LOCK1 instruction for atomicity. This is mainly
+	  provided as a debugging aid if the lnkget/lnkset atomicity primitive
+	  isn't working properly.
+
+endchoice
+
+config META_FPU
+	bool "FPU Support"
+	depends on META21
+	default y
+	help
+	  This option allows processes to use FPU hardware available with this
+	  CPU. If this option is not enabled FPU registers will not be saved
+	  and restored on context-switch.
+
+	  If you plan on running programs which are compiled to use hard floats
+	  say Y here.
+
+config META_DSP
+	bool "DSP Support"
+	help
+	  This option allows processes to use DSP hardware available
+	  with this CPU. If this option is not enabled DSP registers
+	  will not be saved and restored on context-switch.
+
+	  If you plan on running DSP programs say Y here.
+
+config META_PERFCOUNTER_IRQS
+	bool "PerfCounters interrupt support"
+	depends on META21
+	help
+	  This option enables using interrupts to collect information from
+	  Performance Counters. This option is supported in new META21
+	  (starting from HTP265).
+
+	  When disabled, Performance Counters information will be collected
+	  based on Timer Interrupt.
+
+menu "Boot options"
+
+config METAG_CMDLINE_BOOL
+	bool "Built-in kernel command line"
+	---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_METAG_CMDLINE.
+
+	  Systems with fully functional boot loaders (i.e. non-embedded)
+	  should leave this option set to 'N'.
+
+config METAG_CMDLINE
+	string "Built-in kernel command string"
+	depends on METAG_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_METAG_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 METAG_CMDLINE_OVERRIDE
+	bool "Built-in command line overrides boot loader arguments"
+	depends on METAG_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
+
+menu "Board support"
+
+endmenu
+
+source "kernel/Kconfig.preempt"
+
+source kernel/Kconfig.hz
+
+endmenu
+
+menu "Power management options"
+
+source kernel/power/Kconfig
+
+endmenu
+
+menu "Executable file formats"
+
+source "fs/Kconfig.binfmt"
+
+endmenu
+
+source "net/Kconfig"
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+source "arch/metag/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
diff --git a/arch/metag/Kconfig.debug b/arch/metag/Kconfig.debug
new file mode 100644
index 0000000..d2f4e18
--- /dev/null
+++ b/arch/metag/Kconfig.debug
@@ -0,0 +1,40 @@
+menu "Kernel hacking"
+
+config TRACE_IRQFLAGS_SUPPORT
+	bool
+	default y
+
+source "lib/Kconfig.debug"
+
+config DEBUG_STACKOVERFLOW
+	bool "Check for stack overflows"
+	depends on DEBUG_KERNEL
+	help
+	  This option will cause messages to be printed if free stack space
+	  drops below a certain limit.
+
+config 4KSTACKS
+	bool "Use 4Kb for kernel stacks instead of 8Kb"
+	depends on DEBUG_KERNEL
+	help
+	  If you say Y here the kernel will use a 4Kb stacksize for the
+	  kernel stack attached to each process/thread. This facilitates
+	  running more threads on a system and also reduces the pressure
+	  on the VM subsystem for higher order allocations. This option
+	  will also use IRQ stacks to compensate for the reduced stackspace.
+
+config META_FUNCTION_TRACE
+	bool "Output Meta real-time trace data for function entry/exit"
+	help
+	  If you say Y here the kernel will use the Meta hardware trace
+	  unit to output information about function entry and exit that
+	  can be used by a debugger for profiling and call-graphs.
+
+config POISON_CATCH_BUFFERS
+	bool "Poison catch buffer contents on kernel entry"
+	help
+	  If you say Y here the kernel will write poison data to the
+	  catch buffer registers on kernel entry. This will make any
+	  problem with catch buffer handling much more apparent.
+
+endmenu
diff --git a/arch/metag/Kconfig.soc b/arch/metag/Kconfig.soc
new file mode 100644
index 0000000..698653e
--- /dev/null
+++ b/arch/metag/Kconfig.soc
@@ -0,0 +1,58 @@
+choice
+	prompt "SoC Type"
+	default META21_FPGA
+
+config META12_FPGA
+	bool "Meta 1.2 FPGA"
+	select META12
+	---help---
+	  This is a Meta 1.2 FPGA bitstream, just a bare CPU.
+
+config META21_FPGA
+	bool "Meta 2.1 FPGA"
+	select META21
+	---help---
+	  This is a Meta 2.1 FPGA bitstream, just a bare CPU.
+
+endchoice
+
+menu "SoC configuration"
+
+if META21
+
+# Meta 2.x specific options
+
+config META21_MMU
+	bool "Meta 2.x MMU mode"
+	depends on META21
+	default y
+	help
+	  Use the Meta 2.x MMU in extended mode.
+
+config META21_UNALIGNED
+	bool "Meta 2.x unaligned access checking"
+	depends on META21
+	default y
+	help
+	  All memory accesses will be checked for alignment and an exception
+	  raised on unaligned accesses. This feature does cost performance
+	  but without it there will be no notification of this type of error.
+
+config META_USER_TCM
+	bool "Meta on-chip memory support for userland"
+	depends on META21
+	select GENERIC_ALLOCATOR
+	default y
+	help
+	  Allow the on-chip memories of Meta SoCs to be used by user
+	  applications.
+
+endif
+
+config META_HALT_ON_PANIC
+	bool "Halt the core on panic"
+	help
+	  Halt the core when a panic occurs. This is useful when running
+	  pre-production silicon or in an FPGA environment.
+
+endmenu
diff --git a/arch/metag/Makefile b/arch/metag/Makefile
new file mode 100644
index 0000000..7fd59be
--- /dev/null
+++ b/arch/metag/Makefile
@@ -0,0 +1,90 @@
+#
+# metag/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies. Remember to do have actions
+# for "archclean" cleaning up for this architecture.
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1994 by Linus Torvalds
+#               2007,2008,2012 by Imagination Technologies Ltd.
+#
+
+LDFLAGS		:=
+OBJCOPYFLAGS	:= -O binary -R .note -R .comment -S
+
+CHECKFLAGS-$(CONFIG_META12) += -DMETAC_1_2
+CHECKFLAGS-$(CONFIG_META21) += -DMETAC_2_1
+CHECKFLAGS	+= -D__metag__ $(CHECKFLAGS-y)
+
+KBUILD_DEFCONFIG := meta2_defconfig
+
+sflags-$(CONFIG_META12) += -mmetac=1.2
+
+ifeq ($(CONFIG_META12),y)
+# Only use TBI API 1.4 if DSP is enabled for META12 cores
+sflags-$(CONFIG_META_DSP) += -DTBI_1_4
+endif
+
+sflags-$(CONFIG_META21) += -mmetac=2.1 -DTBI_1_4
+
+# Default SoC .c files
+score-y  :=
+
+head-y := arch/metag/kernel/head.o
+
+core-y					+= arch/metag/kernel/ \
+					   arch/metag/mm/
+
+ifneq ($(score-y),)
+core-y                                  += arch/metag/$(score-y)/
+endif
+
+ifneq ($(machdir-y),)
+core-y  += $(addprefix arch/metag/boards/, \
+             $(filter-out ., $(patsubst %,%/,$(machdir-y))))
+endif
+
+libs-y 					+= arch/metag/lib/ \
+					   arch/metag/tbx/
+
+cflags-$(CONFIG_META_FUNCTION_TRACE) +=  -mhwtrace-leaf -mhwtrace-retpc
+
+cflags-$(CONFIG_META21) += -mextensions=bex
+
+KBUILD_CFLAGS += -pipe
+
+KBUILD_CFLAGS += -ffunction-sections
+
+KBUILD_CFLAGS += $(sflags-y) $(cflags-y)
+KBUILD_AFLAGS += $(sflags-y)
+
+LDFLAGS_vmlinux := $(ldflags-y)
+
+boot := arch/metag/boot
+
+BOOT_TARGETS = uImage uImage.bz2 uImage.gz uImage.lzma uImage.xz uImage.lzo \
+	       uImage.bin vmlinux.bin
+PHONY += $(BOOT_TARGETS)
+
+all: vmlinux.bin
+
+$(BOOT_TARGETS): vmlinux
+	$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
+archclean:
+	$(Q)$(MAKE) $(clean)=$(boot)
+
+define archhelp
+  echo  '* vmlinux.bin	- Binary kernel image (arch/$(ARCH)/boot/vmlinux.bin)'
+  @echo '  uImage  	- Alias to bootable U-Boot image'
+  @echo '  uImage.bin	- Kernel-only image for U-Boot (bin)'
+  @echo '  uImage.gz	- Kernel-only image for U-Boot (gzip)'
+  @echo '  uImage.bz2	- Kernel-only image for U-Boot (bzip2)'
+  @echo '  uImage.lzma	- Kernel-only image for U-Boot (lzma)'
+  @echo '  uImage.xz	- Kernel-only image for U-Boot (xz)'
+  @echo '  uImage.lzo	- Kernel-only image for U-Boot (lzo)'
+endef
diff --git a/arch/metag/boot/Makefile b/arch/metag/boot/Makefile
new file mode 100644
index 0000000..9f6fb26
--- /dev/null
+++ b/arch/metag/boot/Makefile
@@ -0,0 +1,72 @@
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2007,2012  Imagination Technologies Ltd.
+#
+
+suffix-y := bin
+suffix-$(CONFIG_KERNEL_GZIP)	:= gz
+suffix-$(CONFIG_KERNEL_BZIP2)	:= bz2
+suffix-$(CONFIG_KERNEL_LZMA)	:= lzma
+suffix-$(CONFIG_KERNEL_XZ)	:= xz
+suffix-$(CONFIG_KERNEL_LZO)	:= lzo
+
+targets := uImage uImage.gz uImage.bz2 uImage.lzma uImage.xz uImage.lzo \
+	   uImage.bin
+extra-y += vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
+	   vmlinux.bin.xz vmlinux.bin.lzo
+
+UIMAGE_LOADADDR = $(CONFIG_PAGE_OFFSET)
+
+VMLINUX = vmlinux
+
+ifeq ($(CONFIG_FUNCTION_TRACER),y)
+ORIG_CFLAGS := $(KBUILD_CFLAGS)
+KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
+endif
+
+$(obj)/vmlinux.bin: $(VMLINUX)
+	$(Q)$(OBJCOPY) -O binary $(strip-flags) $(VMLINUX) $(obj)/vmlinux.bin
+
+$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
+	$(call if_changed,gzip)
+
+$(obj)/vmlinux.bin.bz2: $(obj)/vmlinux.bin FORCE
+	$(call if_changed,bzip2)
+
+$(obj)/vmlinux.bin.lzma: $(obj)/vmlinux.bin FORCE
+	$(call if_changed,lzma)
+
+$(obj)/vmlinux.bin.xz: $(obj)/vmlinux.bin FORCE
+	$(call if_changed,xzkern)
+
+$(obj)/vmlinux.bin.lzo: $(obj)/vmlinux.bin FORCE
+	$(call if_changed,lzo)
+
+$(obj)/uImage.bz2: $(obj)/vmlinux.bin.bz2
+	$(call if_changed,uimage,bzip2)
+
+$(obj)/uImage.gz: $(obj)/vmlinux.bin.gz
+	$(call if_changed,uimage,gzip)
+
+$(obj)/uImage.lzma: $(obj)/vmlinux.bin.lzma
+	$(call if_changed,uimage,lzma)
+
+$(obj)/uImage.xz: $(obj)/vmlinux.bin.xz
+	$(call if_changed,uimage,xz)
+
+$(obj)/uImage.lzo: $(obj)/vmlinux.bin.lzo
+	$(call if_changed,uimage,lzo)
+
+$(obj)/uImage.bin: $(obj)/vmlinux.bin
+	$(call if_changed,uimage,none)
+
+$(obj)/uImage: $(obj)/uImage.$(suffix-y)
+	@ln -sf $(notdir $<) $@
+	@echo '  Image $@ is ready'
+
+clean-files += vmlinux.bin
+
+export suffix-y
diff --git a/arch/metag/configs/meta1_defconfig b/arch/metag/configs/meta1_defconfig
new file mode 100644
index 0000000..eb01dde
--- /dev/null
+++ b/arch/metag/configs/meta1_defconfig
@@ -0,0 +1,42 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_LOG_BUF_SHIFT=13
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE="arch/metag/boot/ramdisk.cpio"
+CONFIG_INITRAMFS_COMPRESSION_GZIP=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_ELF_CORE is not set
+CONFIG_SLAB=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_META12_FPGA=y
+CONFIG_HZ_100=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=1
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_CRC32 is not set
diff --git a/arch/metag/configs/meta2_defconfig b/arch/metag/configs/meta2_defconfig
new file mode 100644
index 0000000..1d28258
--- /dev/null
+++ b/arch/metag/configs/meta2_defconfig
@@ -0,0 +1,42 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_LOG_BUF_SHIFT=13
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE="arch/metag/boot/ramdisk.cpio"
+CONFIG_INITRAMFS_COMPRESSION_GZIP=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_ELF_CORE is not set
+CONFIG_SLAB=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_META_L2C=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_META_HALT_ON_PANIC=y
+CONFIG_HZ_100=y
+CONFIG_DEVTMPFS=y
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=1
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_CRC32 is not set
diff --git a/arch/metag/configs/meta2_smp_defconfig b/arch/metag/configs/meta2_smp_defconfig
new file mode 100644
index 0000000..979107b
--- /dev/null
+++ b/arch/metag/configs/meta2_smp_defconfig
@@ -0,0 +1,43 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_LOG_BUF_SHIFT=13
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE="arch/metag/boot/ramdisk.cpio"
+CONFIG_INITRAMFS_COMPRESSION_GZIP=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_ELF_CORE is not set
+CONFIG_SLAB=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_META_L2C=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_META_HALT_ON_PANIC=y
+CONFIG_SMP=y
+CONFIG_HZ_100=y
+CONFIG_DEVTMPFS=y
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=1
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_CRC32 is not set
diff --git a/arch/metag/include/asm/Kbuild b/arch/metag/include/asm/Kbuild
new file mode 100644
index 0000000..1678d85
--- /dev/null
+++ b/arch/metag/include/asm/Kbuild
@@ -0,0 +1,59 @@
+include include/asm-generic/Kbuild.asm
+
+header-y += tbx/
+
+header-y += hw_breakpoints.h
+
+generic-y += auxvec.h
+generic-y += bitsperlong.h
+generic-y += bugs.h
+generic-y += cputime.h
+generic-y += current.h
+generic-y += device.h
+generic-y += dma.h
+generic-y += emergency-restart.h
+generic-y += errno.h
+generic-y += exec.h
+generic-y += fb.h
+generic-y += fcntl.h
+generic-y += ftrace.h
+generic-y += futex.h
+generic-y += hardirq.h
+generic-y += hw_irq.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += ipcbuf.h
+generic-y += irq_regs.h
+generic-y += kdebug.h
+generic-y += kmap_types.h
+generic-y += kvm_para.h
+generic-y += local.h
+generic-y += local64.h
+generic-y += msgbuf.h
+generic-y += mutex.h
+generic-y += param.h
+generic-y += pci.h
+generic-y += percpu.h
+generic-y += poll.h
+generic-y += posix_types.h
+generic-y += scatterlist.h
+generic-y += sections.h
+generic-y += sembuf.h
+generic-y += serial.h
+generic-y += shmbuf.h
+generic-y += shmparam.h
+generic-y += signal.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += stat.h
+generic-y += statfs.h
+generic-y += switch_to.h
+generic-y += termbits.h
+generic-y += termios.h
+generic-y += timex.h
+generic-y += types.h
+generic-y += ucontext.h
+generic-y += unaligned.h
+generic-y += user.h
+generic-y += vga.h
+generic-y += xor.h
diff --git a/arch/metag/include/asm/tbx/Kbuild b/arch/metag/include/asm/tbx/Kbuild
new file mode 100644
index 0000000..41791e6
--- /dev/null
+++ b/arch/metag/include/asm/tbx/Kbuild
@@ -0,0 +1,8 @@
+header-y += compasm.inc
+header-y += machine.inc
+header-y += metac_1_2.inc
+header-y += metac_2_1.inc
+header-y += metac_all.inc
+header-y += metag.inc
+header-y += metagtbx.h
+
diff --git a/arch/metag/kernel/.gitignore b/arch/metag/kernel/.gitignore
new file mode 100644
index 0000000..c5f676c
--- /dev/null
+++ b/arch/metag/kernel/.gitignore
@@ -0,0 +1 @@
+vmlinux.lds
diff --git a/arch/metag/kernel/Makefile b/arch/metag/kernel/Makefile
new file mode 100644
index 0000000..0391546
--- /dev/null
+++ b/arch/metag/kernel/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile for the Linux/Meta kernel.
+#
+
+extra-y	+= head.o
+extra-y	+= vmlinux.lds
+
+obj-y	+= cachepart.o
+obj-y	+= clock.o
+obj-y	+= core_reg.o
+obj-y	+= dma.o
+obj-y	+= hw_breakpoints.o
+obj-y	+= irq.o
+obj-y	+= irq_external.o
+obj-y	+= kick.o
+obj-y	+= process.o
+obj-y	+= ptrace.o
+obj-y	+= setup.o
+obj-y	+= signal.o
+obj-y	+= stacktrace.o
+obj-y	+= sys_metag.o
+obj-y	+= tbiunexp.o
+obj-y	+= time.o
+obj-y	+= topology.o
+obj-y	+= traps.o
+obj-y	+= user_gateway.o
+
+obj-$(CONFIG_METAG_COREMEM)		+= coremem.o
+obj-$(CONFIG_DYNAMIC_FTRACE)		+= ftrace.o
+obj-$(CONFIG_FUNCTION_TRACER)		+= ftrace_stub.o
+obj-$(CONFIG_META_PERFCOUNTER_IRQS)	+= irq_internal.o
+obj-$(CONFIG_MODULES)			+= metag_ksyms.o
+obj-$(CONFIG_MODULES)			+= module.o
+obj-$(CONFIG_PERF_EVENTS)		+= perf_callchain.o
+obj-$(CONFIG_SMP)			+= smp.o
+obj-$(CONFIG_METAG_SUSPEND_MEM)		+= suspend.o
+obj-$(CONFIG_META_USER_TCM)		+= tcm.o
diff --git a/arch/metag/kernel/asm-offsets.c b/arch/metag/kernel/asm-offsets.c
new file mode 100644
index 0000000..bfc9205
--- /dev/null
+++ b/arch/metag/kernel/asm-offsets.c
@@ -0,0 +1,14 @@
+/*
+ * This program is used to generate definitions needed by
+ * assembly language modules.
+ *
+ */
+
+#include <linux/kbuild.h>
+#include <linux/thread_info.h>
+
+int main(void)
+{
+	DEFINE(THREAD_INFO_SIZE, sizeof(struct thread_info));
+	return 0;
+}
diff --git a/arch/metag/kernel/metag_ksyms.c b/arch/metag/kernel/metag_ksyms.c
new file mode 100644
index 0000000..0ea1332
--- /dev/null
+++ b/arch/metag/kernel/metag_ksyms.c
@@ -0,0 +1,77 @@
+#include <linux/export.h>
+#include <linux/linkage.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/user.h>
+#include <linux/interrupt.h>
+#include <linux/hardirq.h>
+
+#include <asm/setup.h>
+#include <asm/checksum.h>
+#include <asm/uaccess.h>
+#include <asm/traps.h>
+#include <asm/ftrace.h>
+
+#define METAG_ALL_VALUES
+#define METAC_ALL_VALUES
+#include <asm/tbx/metagtbx.h>
+
+/* uaccess symbols */
+EXPORT_SYMBOL(__copy_user_zeroing);
+EXPORT_SYMBOL(__copy_user);
+EXPORT_SYMBOL(__get_user_asm_b);
+EXPORT_SYMBOL(__get_user_asm_w);
+EXPORT_SYMBOL(__get_user_asm_d);
+EXPORT_SYMBOL(__put_user_asm_b);
+EXPORT_SYMBOL(__put_user_asm_w);
+EXPORT_SYMBOL(__put_user_asm_d);
+EXPORT_SYMBOL(__put_user_asm_l);
+EXPORT_SYMBOL(__strncpy_from_user);
+EXPORT_SYMBOL(strnlen_user);
+EXPORT_SYMBOL(__do_clear_user);
+
+EXPORT_SYMBOL(pTBI_get);
+EXPORT_SYMBOL(meta_memoffset);
+EXPORT_SYMBOL(kick_register_func);
+EXPORT_SYMBOL(kick_unregister_func);
+#ifdef CONFIG_SMP
+EXPORT_SYMBOL(get_trigger_mask);
+#else
+EXPORT_SYMBOL(global_trigger_mask);
+#endif
+
+EXPORT_SYMBOL(empty_zero_page);
+EXPORT_SYMBOL(kernel_thread);
+
+EXPORT_SYMBOL(pfn_base);
+
+/* TBI symbols */
+EXPORT_SYMBOL(__TBI);
+EXPORT_SYMBOL(__TBIFindSeg);
+EXPORT_SYMBOL(__TBIPoll);
+EXPORT_SYMBOL(__TBITimeStamp);
+EXPORT_SYMBOL(__TBITransStr);
+EXPORT_SYMBOL(__TBICodeCacheFlush);
+EXPORT_SYMBOL(__TBIDataCacheFlush);
+
+#define DECLARE_EXPORT(name) extern void name(void); EXPORT_SYMBOL(name)
+
+/* libgcc functions */
+DECLARE_EXPORT(__ashldi3);
+DECLARE_EXPORT(__ashrdi3);
+DECLARE_EXPORT(__lshrdi3);
+DECLARE_EXPORT(__udivsi3);
+DECLARE_EXPORT(__divsi3);
+DECLARE_EXPORT(__umodsi3);
+DECLARE_EXPORT(__modsi3);
+DECLARE_EXPORT(__muldi3);
+
+/* String functions */
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memmove);
+
+#ifdef CONFIG_FUNCTION_TRACER
+EXPORT_SYMBOL(mcount);
+EXPORT_SYMBOL(mcount_wrapper);
+#endif
diff --git a/arch/metag/kernel/vmlinux.lds.S b/arch/metag/kernel/vmlinux.lds.S
new file mode 100644
index 0000000..d1ad7cf
--- /dev/null
+++ b/arch/metag/kernel/vmlinux.lds.S
@@ -0,0 +1,71 @@
+/* ld script to make Meta Linux kernel */
+
+#include <asm/thread_info.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+
+#include <asm-generic/vmlinux.lds.h>
+
+OUTPUT_FORMAT("elf32-metag", "elf32-metag", "elf32-metag")
+OUTPUT_ARCH(metag)
+ENTRY(__start)
+
+_jiffies = _jiffies_64;
+SECTIONS
+{
+  . = CONFIG_PAGE_OFFSET;
+  _text = .;
+  __text = .;
+  __stext = .;
+  HEAD_TEXT_SECTION
+  .text : {
+	TEXT_TEXT
+	SCHED_TEXT
+	LOCK_TEXT
+	KPROBES_TEXT
+	IRQENTRY_TEXT
+	*(.text.*)
+	*(.gnu.warning)
+	}
+
+  __etext = .;			/* End of text section */
+
+  __sdata = .;
+  RO_DATA_SECTION(PAGE_SIZE)
+  RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
+  __edata = .;			/* End of data section */
+
+  EXCEPTION_TABLE(16)
+  NOTES
+
+  . = ALIGN(PAGE_SIZE);		/* Init code and data */
+  ___init_begin = .;
+  INIT_TEXT_SECTION(PAGE_SIZE)
+  INIT_DATA_SECTION(16)
+
+  .init.arch.info : {
+	  ___arch_info_begin = .;
+	  *(.arch.info.init)
+	  ___arch_info_end = .;
+  }
+
+  PERCPU_SECTION(L1_CACHE_BYTES)
+
+  ___init_end = .;
+
+  BSS_SECTION(0, PAGE_SIZE, 0)
+
+  __end = .;
+	
+  . = ALIGN(PAGE_SIZE);
+  __heap_start = .;
+
+  DWARF_DEBUG
+
+  /* When something in the kernel is NOT compiled as a module, the
+   * module cleanup code and data are put into these segments.  Both
+   * can then be thrown away, as cleanup code is never called unless
+   * it's a module.
+   */
+  DISCARDS
+}
diff --git a/arch/metag/lib/Makefile b/arch/metag/lib/Makefile
new file mode 100644
index 0000000..d2f9439
--- /dev/null
+++ b/arch/metag/lib/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Meta-specific library files.
+#
+
+lib-y = usercopy.o copy_page.o clear_page.o memcpy.o memmove.o \
+	memset.o delay.o div64.o muldi3.o ashrdi3.o ashldi3.o lshrdi3.o \
+	divsi3.o modsi3.o ip_fast_csum.o checksum.o
diff --git a/arch/metag/mm/Kconfig b/arch/metag/mm/Kconfig
new file mode 100644
index 0000000..e5055b8
--- /dev/null
+++ b/arch/metag/mm/Kconfig
@@ -0,0 +1,153 @@
+menu "Memory management options"
+
+config PAGE_OFFSET
+	hex "Kernel page offset address"
+	default "0x40000000"
+	help
+	  This option allows you to set the virtual address at which the
+	  kernel will be mapped to.
+endmenu
+
+config KERNEL_4M_PAGES
+	bool "Map kernel with 4MB pages"
+	depends on META21_MMU
+	default y
+	help
+	  Map the kernel with large pages to reduce TLB pressure.
+
+choice
+	prompt "User page size"
+	default PAGE_SIZE_4K
+
+config PAGE_SIZE_4K
+	bool "4kB"
+	help
+	  This is the default page size used by all Meta cores.
+
+config PAGE_SIZE_8K
+	bool "8kB"
+	depends on META21_MMU
+	help
+	  This enables 8kB pages as supported by Meta 2.x and later MMUs.
+
+config PAGE_SIZE_16K
+	bool "16kB"
+	depends on META21_MMU
+	help
+	  This enables 16kB pages as supported by Meta 2.x and later MMUs.
+
+endchoice
+
+config NUMA
+	bool "Non Uniform Memory Access (NUMA) Support"
+	help
+	  Some Meta systems have MMU-mappable on-chip memories with
+	  lower latencies than main memory. This enables support for
+	  these blocks by binding them to nodes and allowing
+	  memory policies to be used for prioritizing and controlling
+	  allocation behaviour.
+
+config FORCE_MAX_ZONEORDER
+	int "Maximum zone order"
+	range 10 32
+	default "10"
+	help
+	  The kernel memory allocator divides physically contiguous memory
+	  blocks into "zones", where each zone is a power of two number of
+	  pages.  This option selects the largest power of two that the kernel
+	  keeps in the memory allocator.  If you need to allocate very large
+	  blocks of physically contiguous memory, then you may need to
+	  increase this value.
+
+	  This config option is actually maximum order plus one. For example,
+	  a value of 11 means that the largest free memory block is 2^10 pages.
+
+	  The page size is not necessarily 4KB.  Keep this in mind
+	  when choosing a value for this option.
+
+config META_L2C
+	bool "Level 2 Cache Support"
+	depends on META21
+	help
+	  Press y here to enable support for the Meta Level 2 (L2) cache. This
+	  will enable the cache at start up if it hasn't already been enabled
+	  by the bootloader.
+
+	  If the bootloader enables the L2 you must press y here to ensure the
+	  kernel takes the appropriate actions to keep the cache coherent.
+
+config NODES_SHIFT
+	int
+	default "1"
+	depends on NEED_MULTIPLE_NODES
+
+config ARCH_FLATMEM_ENABLE
+	def_bool y
+	depends on !NUMA
+
+config ARCH_SPARSEMEM_ENABLE
+	def_bool y
+	select SPARSEMEM_STATIC
+
+config ARCH_SPARSEMEM_DEFAULT
+	def_bool y
+
+config MAX_ACTIVE_REGIONS
+	int
+	default "2" if SPARSEMEM
+	default "1"
+
+config ARCH_POPULATES_NODE_MAP
+	def_bool y
+
+config ARCH_SELECT_MEMORY_MODEL
+	def_bool y
+
+config SYS_SUPPORTS_HUGETLBFS
+	def_bool y
+	depends on META21_MMU
+
+choice
+	prompt "HugeTLB page size"
+	depends on META21_MMU && HUGETLB_PAGE
+	default HUGETLB_PAGE_SIZE_1M
+
+config HUGETLB_PAGE_SIZE_8K
+	bool "8kB"
+	depends on PAGE_SIZE_4K
+
+config HUGETLB_PAGE_SIZE_16K
+	bool "16kB"
+	depends on PAGE_SIZE_4K || PAGE_SIZE_8K
+
+config HUGETLB_PAGE_SIZE_32K
+	bool "32kB"
+
+config HUGETLB_PAGE_SIZE_64K
+	bool "64kB"
+
+config HUGETLB_PAGE_SIZE_128K
+	bool "128kB"
+
+config HUGETLB_PAGE_SIZE_256K
+	bool "256kB"
+
+config HUGETLB_PAGE_SIZE_512K
+	bool "512kB"
+
+config HUGETLB_PAGE_SIZE_1M
+	bool "1MB"
+
+config HUGETLB_PAGE_SIZE_2M
+	bool "2MB"
+
+config HUGETLB_PAGE_SIZE_4M
+	bool "4MB"
+
+endchoice
+
+config METAG_COREMEM
+	bool
+	default y if SUSPEND
+
+source "mm/Kconfig"
diff --git a/arch/metag/mm/Makefile b/arch/metag/mm/Makefile
new file mode 100644
index 0000000..097f104
--- /dev/null
+++ b/arch/metag/mm/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the linux Meta-specific parts of the memory manager.
+#
+
+obj-y	:= init.o fault.o ioremap.o extable.o maccess.o
+
+obj-$(CONFIG_NUMA)	+= numa.o
+obj-$(CONFIG_HIGHMEM)	+= highmem.o
+obj-$(CONFIG_META_L2C)	+= l2cache.o
+
+ifdef CONFIG_META21_MMU
+obj-y 	+= mmu-meta2.o
+else
+obj-y 	+= mmu-meta1.o
+endif
+
+obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
diff --git a/arch/metag/tbx/Makefile b/arch/metag/tbx/Makefile
new file mode 100644
index 0000000..478e193
--- /dev/null
+++ b/arch/metag/tbx/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for TBX library files..
+#
+
+asflags-y += -I$(srctree)/arch/metag/include/asm/tbx -mmetac=2.1 \
+		-Wa,-mfpu=metac21 -mdsp
+ccflags-y += -I$(srctree)/arch/metag/include/asm/tbx -mmetac=2.1
+
+ifeq ($(CONFIG_SMP),y)
+asflags-y += -DTBX_PERCPU_SP_SAVE
+endif
+
+lib-y = tbicache.o tbicore.o tbictx.o tbilogf.o \
+	tbipcx.o tbiroot.o tbisoft.o tbistring.o tbitimer.o tbidefr.o
+
+lib-$(CONFIG_META_DSP) += tbidspram.o
+lib-$(CONFIG_META_FPU) += tbictxfpu.o
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2403a63..6a73b08 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -452,7 +452,7 @@ config SLUB_STATS
 config DEBUG_KMEMLEAK
 	bool "Kernel memory leak detector"
 	depends on DEBUG_KERNEL && EXPERIMENTAL && \
-		(X86 || ARM || PPC || MIPS || S390 || SPARC64 || SUPERH || MICROBLAZE || TILE)
+		(X86 || ARM || PPC || MIPS || S390 || SPARC64 || SUPERH || MICROBLAZE || TILE || METAG)
 
 	select DEBUG_FS
 	select STACKTRACE if STACKTRACE_SUPPORT
@@ -714,7 +714,7 @@ config STACKTRACE
 
 config DEBUG_STACK_USAGE
 	bool "Stack utilization instrumentation"
-	depends on DEBUG_KERNEL && !IA64 && !PARISC
+	depends on DEBUG_KERNEL && !IA64 && !PARISC && !METAG
 	help
 	  Enables the display of the minimum amount of free stack which each
 	  task has ever had available in the sysrq-T and sysrq-P debug output.
@@ -885,7 +885,7 @@ config FRAME_POINTER
 	bool "Compile the kernel with frame pointers"
 	depends on DEBUG_KERNEL && \
 		(CRIS || M68K || FRV || UML || \
-		 AVR32 || SUPERH || BLACKFIN || MN10300) || \
+		 AVR32 || SUPERH || BLACKFIN || MN10300 || METAG) || \
 		ARCH_WANT_FRAME_POINTERS
 	default y if (DEBUG_INFO && UML) || ARCH_WANT_FRAME_POINTERS
 	help
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 13/40] metag: Device tree
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (9 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 12/40] metag: Build infrastructure James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 14/40] metag: Ptrace James Hogan
                   ` (29 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add device tree files to arch/metag.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/Kconfig                |   13 +++++
 arch/metag/Makefile               |    9 +++
 arch/metag/boot/dtb/Makefile      |   32 ++++++++++++
 arch/metag/boot/dts/skeleton.dts  |   10 ++++
 arch/metag/boot/dts/skeleton.dtsi |   14 +++++
 arch/metag/include/asm/prom.h     |   23 +++++++++
 arch/metag/kernel/Makefile        |    2 +
 arch/metag/kernel/devtree.c       |  100 +++++++++++++++++++++++++++++++++++++
 8 files changed, 203 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/boot/dtb/Makefile
 create mode 100644 arch/metag/boot/dts/skeleton.dts
 create mode 100644 arch/metag/boot/dts/skeleton.dtsi
 create mode 100644 arch/metag/include/asm/prom.h
 create mode 100644 arch/metag/kernel/devtree.c

diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index 3a19cb6..5141108 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -210,6 +210,19 @@ config META_PERFCOUNTER_IRQS
 
 menu "Boot options"
 
+config METAG_BUILTIN_DTB
+	bool "Embed DTB in kernel image"
+	default y
+	help
+	  Embeds a device tree binary in the kernel image.
+
+config METAG_BUILTIN_DTB_NAME
+	string "Built in DTB"
+	depends on METAG_BUILTIN_DTB
+	help
+	  Set the name of the DTB to embed (leave blank to pick one
+	  automatically based on kernel configuration).
+
 config METAG_CMDLINE_BOOL
 	bool "Built-in kernel command line"
 	---help---
diff --git a/arch/metag/Makefile b/arch/metag/Makefile
index 7fd59be..aae38f6 100644
--- a/arch/metag/Makefile
+++ b/arch/metag/Makefile
@@ -22,6 +22,8 @@ CHECKFLAGS	+= -D__metag__ $(CHECKFLAGS-y)
 
 KBUILD_DEFCONFIG := meta2_defconfig
 
+core-y += arch/metag/boot/dtb/
+
 sflags-$(CONFIG_META12) += -mmetac=1.2
 
 ifeq ($(CONFIG_META12),y)
@@ -75,6 +77,12 @@ all: vmlinux.bin
 $(BOOT_TARGETS): vmlinux
 	$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
 
+%.dtb %.dtb.S %.dtb.o:
+	$(Q)$(MAKE) $(build)=$(boot)/dtb $(boot)/dtb/$@
+
+dtbs:
+	$(Q)$(MAKE) $(build)=$(boot)/dtb $(boot)/dtb/$@
+
 archclean:
 	$(Q)$(MAKE) $(clean)=$(boot)
 
@@ -87,4 +95,5 @@ define archhelp
   @echo '  uImage.lzma	- Kernel-only image for U-Boot (lzma)'
   @echo '  uImage.xz	- Kernel-only image for U-Boot (xz)'
   @echo '  uImage.lzo	- Kernel-only image for U-Boot (lzo)'
+  @echo '  dtbs		- Build device tree blobs for enabled boards'
 endef
diff --git a/arch/metag/boot/dtb/Makefile b/arch/metag/boot/dtb/Makefile
new file mode 100644
index 0000000..c84e1fc
--- /dev/null
+++ b/arch/metag/boot/dtb/Makefile
@@ -0,0 +1,32 @@
+dtb-y += skeleton.dtb
+dtb-y += tz1090_generic.dtb
+dtb-y += tz1090_01ry.dtb
+dtb-y += tz1090_01sp.dtb
+dtb-y += tz1090_01tt.dtb
+dtb-y += tz1090_01xk.dtb
+
+# Built-in dtb
+builtindtb-y := skeleton
+builtindtb-$(CONFIG_SOC_COMET) := tz1090_generic
+builtindtb-$(CONFIG_COMET_BUB) := tz1090_01ry
+builtindtb-$(CONFIG_TZ1090_01XX) := tz1090_01sp
+builtindtb-$(CONFIG_POLARIS) := tz1090_01xk
+
+ifeq ($(CONFIG_METAG_BUILTIN_DTB),y)
+ifneq '$(CONFIG_METAG_BUILTIN_DTB_NAME)' '""'
+BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_METAG_BUILTIN_DTB_NAME)).dtb.o
+else
+BUILTIN_DTB := $(patsubst "%",%,$(builtindtb-y)).dtb.o
+endif
+else
+BUILTIN_DTB :=
+endif
+obj-y += $(BUILTIN_DTB)
+
+clean-files += $(obj)/$(dtb-y) $(obj)/*.dtb.S $(obj)/*.dtb.o
+
+# Rule to build device tree blobs
+$(obj)/%.dtb: $(src)/../dts/%.dts FORCE
+	$(call if_changed_dep,dtc)
+
+$(obj)/dtbs: $(addprefix $(obj)/, $(dtb-y))
diff --git a/arch/metag/boot/dts/skeleton.dts b/arch/metag/boot/dts/skeleton.dts
new file mode 100644
index 0000000..7244d1f
--- /dev/null
+++ b/arch/metag/boot/dts/skeleton.dts
@@ -0,0 +1,10 @@
+/*
+ * Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+/dts-v1/;
+
+/include/ "skeleton.dtsi"
diff --git a/arch/metag/boot/dts/skeleton.dtsi b/arch/metag/boot/dts/skeleton.dtsi
new file mode 100644
index 0000000..78229ea
--- /dev/null
+++ b/arch/metag/boot/dts/skeleton.dtsi
@@ -0,0 +1,14 @@
+/*
+ * Skeleton device tree; the bare minimum needed to boot; just include and
+ * add a compatible value.  The bootloader will typically populate the memory
+ * node.
+ */
+
+/ {
+	compatible = "img,meta";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	chosen { };
+	aliases { };
+	memory { device_type = "memory"; reg = <0 0>; };
+};
diff --git a/arch/metag/include/asm/prom.h b/arch/metag/include/asm/prom.h
new file mode 100644
index 0000000..d881396
--- /dev/null
+++ b/arch/metag/include/asm/prom.h
@@ -0,0 +1,23 @@
+/*
+ *  arch/metag/include/asm/prom.h
+ *
+ *  Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ *  Based on ARM version:
+ *  Copyright (C) 2009 Canonical Ltd. <jeremy.kerr@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#ifndef __ASM_METAG_PROM_H
+#define __ASM_METAG_PROM_H
+
+#include <asm/setup.h>
+#define HAVE_ARCH_DEVTREE_FIXUPS
+
+extern struct machine_desc *setup_machine_fdt(void *dt);
+extern void metag_dt_memblock_reserve(void);
+
+#endif /* __ASM_METAG_PROM_H */
diff --git a/arch/metag/kernel/Makefile b/arch/metag/kernel/Makefile
index 0391546..5482817 100644
--- a/arch/metag/kernel/Makefile
+++ b/arch/metag/kernel/Makefile
@@ -8,11 +8,13 @@ extra-y	+= vmlinux.lds
 obj-y	+= cachepart.o
 obj-y	+= clock.o
 obj-y	+= core_reg.o
+obj-y	+= devtree.o
 obj-y	+= dma.o
 obj-y	+= hw_breakpoints.o
 obj-y	+= irq.o
 obj-y	+= irq_external.o
 obj-y	+= kick.o
+obj-y	+= machines.o
 obj-y	+= process.o
 obj-y	+= ptrace.o
 obj-y	+= setup.o
diff --git a/arch/metag/kernel/devtree.c b/arch/metag/kernel/devtree.c
new file mode 100644
index 0000000..251819c
--- /dev/null
+++ b/arch/metag/kernel/devtree.c
@@ -0,0 +1,100 @@
+/*
+ *  linux/arch/metag/kernel/devtree.c
+ *
+ *  Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ *  Based on ARM version:
+ *  Copyright (C) 2009 Canonical Ltd. <jeremy.kerr@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/bootmem.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/mach/arch.h>
+
+void __init early_init_dt_add_memory_arch(u64 base, u64 size)
+{
+	pr_err("%s(%llx, %llx)\n",
+	       __func__, base, size);
+}
+
+void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
+{
+	return alloc_bootmem_align(size, align);
+}
+
+/**
+ * setup_machine_fdt - Machine setup when an dtb was passed to the kernel
+ * @dt:		virtual address pointer to dt blob
+ *
+ * If a dtb was passed to the kernel, then use it to choose the correct
+ * machine_desc and to setup the system.
+ */
+struct machine_desc * __init setup_machine_fdt(void *dt)
+{
+	struct boot_param_header *devtree = dt;
+	struct machine_desc *mdesc, *mdesc_best = NULL;
+	unsigned int score, mdesc_score = ~1;
+	unsigned long dt_root;
+	const char *model;
+
+	/* check device tree validity */
+	if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
+		return NULL;
+
+	/* Search the mdescs for the 'best' compatible value match */
+	initial_boot_params = devtree;
+	dt_root = of_get_flat_dt_root();
+
+	for_each_machine_desc(mdesc) {
+		score = of_flat_dt_match(dt_root, mdesc->dt_compat);
+		if (score > 0 && score < mdesc_score) {
+			mdesc_best = mdesc;
+			mdesc_score = score;
+		}
+	}
+	if (!mdesc_best) {
+		const char *prop;
+		long size;
+
+		pr_err("\nError: unrecognized/unsupported device tree compatible list:\n[ ");
+
+		prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
+		if (prop) {
+			while (size > 0) {
+				printk("'%s' ", prop);
+				size -= strlen(prop) + 1;
+				prop += strlen(prop) + 1;
+			}
+		}
+		printk("]\n\n");
+
+		dump_machine_table(); /* does not return */
+	}
+
+	model = of_get_flat_dt_prop(dt_root, "model", NULL);
+	if (!model)
+		model = of_get_flat_dt_prop(dt_root, "compatible", NULL);
+	if (!model)
+		model = "<unknown>";
+	pr_info("Machine: %s, model: %s\n", mdesc_best->name, model);
+
+	/* Retrieve various information from the /chosen node */
+	of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
+
+	return mdesc_best;
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 14/40] metag: Ptrace
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (10 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 13/40] metag: Device tree James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-11-08  7:17   ` Jonas Bonn
  2012-10-31 16:13 ` [RFC PATCH v1 15/40] metag: Time keeping James Hogan
                   ` (28 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

We use the non-generic interface at the moment (exposing the TBX context
structures to userland), pending conversion to the generic ptrace
interface.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/hw_breakpoints.h |   30 ++
 arch/metag/include/asm/ptrace.h         |   50 +++
 arch/metag/kernel/hw_breakpoints.c      |  102 +++++++
 arch/metag/kernel/ptrace.c              |  503 +++++++++++++++++++++++++++++++
 4 files changed, 685 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/hw_breakpoints.h
 create mode 100644 arch/metag/include/asm/ptrace.h
 create mode 100644 arch/metag/kernel/hw_breakpoints.c
 create mode 100644 arch/metag/kernel/ptrace.c

diff --git a/arch/metag/include/asm/hw_breakpoints.h b/arch/metag/include/asm/hw_breakpoints.h
new file mode 100644
index 0000000..80c46ac
--- /dev/null
+++ b/arch/metag/include/asm/hw_breakpoints.h
@@ -0,0 +1,30 @@
+#ifndef __HW_BREAKPOINT_H__
+#define __HW_BREAKPOINT_H__
+
+enum {
+	META_HWBP_CONT_REGS_BASE = CODEB0ADDR,
+	META_HWBP_CONT_REGS_STRIDE = 8,
+	META_HWBP_CONT_REGS_COUNT = 20,
+	META_HWBP_WRITTEN = 1,
+	META_HWBP_DATA_END = -1
+};
+
+#ifdef __KERNEL__
+
+struct meta_hw_breakpoint_item {
+	unsigned long ctx;
+	unsigned int next;
+};
+
+struct meta_hw_breakpoint {
+	struct meta_hw_breakpoint_item bp[META_HWBP_CONT_REGS_COUNT];
+	unsigned int written, start;
+};
+
+struct meta_hw_breakpoint *create_hwbp(void);
+void setup_hwbp_controller(struct meta_hw_breakpoint *hwbp);
+void restore_hwbp_controller(struct meta_hw_breakpoint *hwbp);
+
+#endif
+
+#endif /* __HW_BREAKPOINT_H__ */
diff --git a/arch/metag/include/asm/ptrace.h b/arch/metag/include/asm/ptrace.h
new file mode 100644
index 0000000..2d29ad0
--- /dev/null
+++ b/arch/metag/include/asm/ptrace.h
@@ -0,0 +1,50 @@
+#ifndef _METAG_PTRACE_H
+#define _METAG_PTRACE_H
+
+#define METAG_ALL_VALUES
+#define METAC_ALL_VALUES
+#include <asm/tbx/machine.inc>
+#include <asm/tbx/metagtbx.h>
+
+#define PTRACE_GETREGS			12
+#define PTRACE_SETREGS			13
+/* x86 use the below numbers */
+#define PTRACE_GETFPREGS		14
+#define PTRACE_SETFPREGS		15
+/* 16 & 17 are earmarked by generic as PTRACE_ATTACH/DETACH respectively */
+
+#define PTRACE_CLEAR_BP			-1
+#define PTRACE_PEEK_BP			100
+#define PTRACE_POKE_BP			101
+#define PTRACE_GETEXTREGS		102
+#define PTRACE_SETEXTREGS		103
+
+
+
+#ifndef __ASSEMBLY__
+
+/* this struct defines the way the registers are stored on the
+   stack during a system call. */
+
+struct pt_regs {
+	TBICTX ctx;
+	TBICTXEXTCB0 extcb0[5];
+};
+
+#ifdef __KERNEL__
+
+#define user_mode(regs) (((regs)->ctx.SaveMask & TBICTX_PRIV_BIT) > 0)
+
+#define instruction_pointer(regs) ((unsigned long)(regs)->ctx.CurrPC)
+#define profile_pc(regs) instruction_pointer(regs)
+
+#define task_pt_regs(task) \
+	((struct pt_regs *)(task_stack_page(task) + \
+			    sizeof(struct thread_info)))
+
+int syscall_trace_enter(struct pt_regs *regs);
+void syscall_trace_leave(struct pt_regs *regs);
+
+#endif /* __KERNEL__ */
+#endif /* __ASSEMBLY__ */
+#endif /* _METAG_PTRACE_H */
diff --git a/arch/metag/kernel/hw_breakpoints.c b/arch/metag/kernel/hw_breakpoints.c
new file mode 100644
index 0000000..bd44b58
--- /dev/null
+++ b/arch/metag/kernel/hw_breakpoints.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008,2010,2012 Imagination Technologies Ltd.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <asm/hw_breakpoints.h>
+
+/*
+ * Allocate and initialise a breakpoint control block
+ */
+struct meta_hw_breakpoint *create_hwbp(void)
+{
+	struct meta_hw_breakpoint *temp;
+
+	temp = kzalloc(sizeof(struct meta_hw_breakpoint), GFP_KERNEL);
+	if (temp)
+		temp->start = META_HWBP_DATA_END;
+
+	return temp;
+}
+
+/*
+ * Sets up the hardware breakpoint controller
+ * only write to the registers that we have data for
+ * having first saved what was there before
+ */
+void setup_hwbp_controller(struct meta_hw_breakpoint *hwbp)
+{
+	unsigned long address, temp;
+	unsigned int reg;
+
+	reg = hwbp->start;
+	while (reg != META_HWBP_DATA_END) {
+		address = (META_HWBP_CONT_REGS_BASE +
+			   (META_HWBP_CONT_REGS_STRIDE * reg));
+		temp = readl(address);
+		writel(hwbp->bp[reg].ctx, address);
+		hwbp->bp[reg].ctx = temp;
+		hwbp->written |= META_HWBP_WRITTEN;
+		reg = hwbp->bp[reg].next;
+	}
+}
+
+/*
+ * Restores the hw break controller to it's original condition
+ * Only restore if we've previously written to it
+ * and only restore the registers we wrote to
+ * Save the current state of the controller before restoring the original values
+ */
+void restore_hwbp_controller(struct meta_hw_breakpoint *hwbp)
+{
+	unsigned long address, temp;
+	unsigned int reg;
+
+	reg = hwbp->start;
+	while (reg != META_HWBP_DATA_END) {
+		address = (META_HWBP_CONT_REGS_BASE +
+			   (META_HWBP_CONT_REGS_STRIDE * reg));
+		temp = readl(address);
+		writel(hwbp->bp[reg].ctx, address);
+		hwbp->bp[reg].ctx = temp;
+		reg = hwbp->bp[reg].next;
+	}
+	hwbp->written &= ~META_HWBP_WRITTEN;
+}
+
+/*
+ * Clear the hardware breakpoint controller by
+ * writing the original values back to it
+ * We only restore the registers we've disturbed
+ * And free the breakpoint data
+ */
+static void clear_hwbp_controller(struct meta_hw_breakpoint *hwbp)
+{
+	unsigned long address;
+	unsigned int reg;
+
+	if (hwbp) {
+		reg = hwbp->start;
+		while (reg != META_HWBP_DATA_END) {
+			address = (META_HWBP_CONT_REGS_BASE +
+				   (META_HWBP_CONT_REGS_STRIDE * reg));
+			writel(hwbp->bp[reg].ctx, address);
+			reg = hwbp->bp[reg].next;
+		}
+		kfree(hwbp);
+	}
+}
+
+/*
+ * Hook into the exit function to clear the breakpoint controller
+ */
+void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+	struct meta_hw_breakpoint *hwbp = tsk->thread.hwbp_context;
+
+	tsk->thread.hwbp_context = NULL;
+	clear_hwbp_controller(hwbp);
+}
diff --git a/arch/metag/kernel/ptrace.c b/arch/metag/kernel/ptrace.c
new file mode 100644
index 0000000..d9cc60f
--- /dev/null
+++ b/arch/metag/kernel/ptrace.c
@@ -0,0 +1,503 @@
+/*
+ *  Copyright (C) 2005,2006,2007,2008,2009,2012 Imagination Technologies Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License.  See the file COPYING in the main directory of
+ * this archive for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/regset.h>
+#include <linux/tracehook.h>
+#include <linux/elf.h>
+#include <linux/uaccess.h>
+#include <trace/syscall.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/syscalls.h>
+
+enum metag_regset {
+	REGSET_GENERAL,
+	REGSET_FP,
+	REGSET_EXT,
+};
+
+
+/*
+ * Read 32-bit data from bp context in the task struct
+ */
+static int ptrace_read_bp(struct task_struct *tsk, unsigned long addr,
+			    unsigned long __user *data)
+{
+	unsigned long tmp = 0;
+
+	if (tsk->thread.hwbp_context)
+		tmp = tsk->thread.hwbp_context->bp[addr].ctx;
+
+	return put_user(tmp, data);
+}
+
+/*
+ * Write 32-bit data to bp context in the task struct
+ * creating the struct if required
+ * return 0 success, 1 on fail
+ */
+static int ptrace_write_bp(struct task_struct *tsk, unsigned long addr,
+			     unsigned long data)
+{
+
+	if (!tsk->thread.hwbp_context) {
+		tsk->thread.hwbp_context = create_hwbp();
+		if (!tsk->thread.hwbp_context)
+			return 1;
+	}
+
+	if (addr == PTRACE_CLEAR_BP) {
+		tsk->thread.hwbp_context->start = META_HWBP_DATA_END;
+		return 0;
+	 }
+
+	tsk->thread.hwbp_context->bp[addr].next = tsk->thread.hwbp_context->start;
+	tsk->thread.hwbp_context->start = addr;
+	tsk->thread.hwbp_context->bp[addr].ctx = data;
+	return 0;
+}
+
+/*
+ * Read an actual register from the tast_struct area.
+ */
+static int __peek_user(struct task_struct *task, int offset)
+{
+	int *regp;
+
+	/* Find stored regblock in stack after task struct */
+	regp = (int *) (task_pt_regs(task));
+
+	/* Align to 8 bytes - Meta stacks are always 8byte aligned */
+	regp = (int *) (((int) regp + 7) & ~7UL);
+
+	return *(int *) ((int) regp + offset);
+}
+
+/*
+ * Write contents of register REGNO in task TASK.
+ */
+static int __poke_user(struct task_struct *task, int offset, unsigned long data)
+{
+	int *regp;
+
+	/* Find stored regblock in stack after task struct */
+	regp = (int *) (task_pt_regs(task));
+
+	/* Align to 8 bytes - Meta stacks are always 8byte aligned */
+	regp = (int *) (((int) regp + 7) & ~7UL);
+
+	*(int *) ((int) regp + offset) = (int) data;
+	return 0;
+}
+
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+void ptrace_disable(struct task_struct *child)
+{
+	/* nothing to do.. */
+}
+
+/*
+ * Read the word at offset "off" into the "struct user".  We
+ * actually access the pt_regs stored on the kernel stack.
+ */
+static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
+			    unsigned long __user *data)
+{
+	unsigned long tmp = 0;
+
+	if (off & 3)
+		return -EIO;
+
+	if (off < sizeof(struct pt_regs))
+		tmp = __peek_user(tsk, off);
+
+	return put_user(tmp, data);
+}
+
+static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
+			     unsigned long data)
+{
+	int ret = -EIO;
+
+	if (off & 3)
+		return -EIO;
+
+	if (off < sizeof(struct pt_regs)) {
+		if (__poke_user(tsk, off, data))
+			return -EIO;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/*
+ * user_regset definitions.
+ */
+
+static int metag_regs_get(struct task_struct *target,
+			  const struct user_regset *regset,
+			  unsigned int pos, unsigned int count,
+			  void *kbuf, void __user *ubuf)
+{
+	if (kbuf) {
+		unsigned long *k = kbuf;
+		while (count > 0) {
+			*k++ = __peek_user(target, pos);
+			count -= sizeof(*k);
+			pos += sizeof(*k);
+		}
+	} else {
+		unsigned long __user *u = ubuf;
+		while (count > 0) {
+			if (__put_user(__peek_user(target, pos), u++))
+				return -EFAULT;
+			count -= sizeof(*u);
+			pos += sizeof(*u);
+		}
+	}
+	return 0;
+}
+
+static int metag_regs_set(struct task_struct *target,
+			  const struct user_regset *regset,
+			  unsigned int pos, unsigned int count,
+			  const void *kbuf, const void __user *ubuf)
+{
+	int rc = 0;
+
+	if (kbuf) {
+		const unsigned long *k = kbuf;
+		while (count > 0 && !rc) {
+			rc = __poke_user(target, pos, *k++);
+			count -= sizeof(*k);
+			pos += sizeof(*k);
+		}
+	} else {
+		const unsigned long  __user *u = ubuf;
+		while (count > 0 && !rc) {
+			unsigned long word;
+			rc = __get_user(word, u++);
+			if (rc)
+				break;
+			rc = __poke_user(target, pos, word);
+			count -= sizeof(*u);
+			pos += sizeof(*u);
+		}
+	}
+
+	return rc;
+}
+
+#ifdef CONFIG_META_DSP
+/*
+ * Expand the saved TBI extended context into a kernel extended context
+ * structure. This makes it easier to process by userspace applications.
+ */
+static void expand_ext_context(int flags, struct meta_ext_context *in,
+			       struct meta_ext_context *out)
+{
+	/*
+	 * We assume that the following flags are set:
+	 *
+	 * TBICTX_XEXT_BIT - if this wasn't then we'd not have an extended
+	 *      context to expand.
+	 * TBICTX_XMCC_BIT - a bit set to signify that this task is a MECC
+	 *      task, however has no real use in the kernel.
+	 *
+	 * However, if the extended context bit is not set, then silently
+	 * leave.
+	 */
+	unsigned char *pos = (unsigned char *)in;
+
+	if (!(flags & TBICTX_XEXT_BIT))
+		return;
+
+	/* Copy the TBICTX_XEXT context */
+	memcpy(&(out->regs.ctx), pos, sizeof(TBIEXTCTX));
+	pos += sizeof(TBIEXTCTX);
+
+	/* Copy the TBICTX_XDX8 registers */
+	if (flags & TBICTX_XDX8_BIT) {
+		memcpy(&(out->regs.bb8), pos, sizeof(TBICTXEXTBB8));
+		pos += sizeof(TBICTXEXTBB8);
+	}
+
+	/* Ax.4 -> Ax.7 context */
+	if (flags & TBICTX_XAXX_BIT) {
+		memcpy(&(out->regs.ax[0]), pos, TBICTXEXTAXX_BYTES);
+		pos += TBICTXEXTAXX_BYTES;
+	}
+
+	/* Hardware loop */
+	if (flags & TBICTX_XHL2_BIT) {
+		memcpy(&(out->regs.hl2), pos, sizeof(TBICTXEXTHL2));
+		pos += sizeof(TBICTXEXTHL2);
+	}
+
+	/* Per-thread DSP registers */
+	if (flags & TBICTX_XTDP_BIT) {
+		memcpy(&(out->regs.ext), pos, sizeof(TBICTXEXTTDPR));
+		pos += sizeof(TBICTXEXTTDPR);
+	}
+
+	/*
+	 * There are two pointer variables in the meta_dsp_context which are
+	 * of no immediate interest to us at this moment in time:
+	 *      void *ram[2], and
+	 *      unsigned int ram_sz[2].
+	 * These describe the address and size of the DSP RAMs used in the
+	 * task - whether they are useful is a poignant question.
+	 */
+}
+
+static int metag_ext_ctx_get(struct task_struct *target,
+			  const struct user_regset *regset,
+			  unsigned int pos, unsigned int count,
+			  void *kbuf, void __user *ubuf)
+{
+	unsigned long dsp_size = sizeof(struct meta_ext_context);
+	struct meta_ext_context *dsp_ctx;
+	int ret = 0;
+
+	if (target->thread.dsp_context) {
+		dsp_ctx = kzalloc(dsp_size, GFP_KERNEL);
+		if (!dsp_ctx) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		/* Expand the DSP context if necessary */
+		expand_ext_context((target->thread.user_flags >> 16),
+				target->thread.dsp_context, dsp_ctx);
+		if (kbuf) {
+			unsigned long *k = kbuf;
+			memcpy(k, dsp_ctx, dsp_size);
+		} else {
+			unsigned long __user *u = ubuf;
+			ret = copy_to_user(u, dsp_ctx, dsp_size);
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(dsp_ctx);
+	}
+out:
+	return ret;
+}
+
+static int compress_ext_context(unsigned int flags, struct meta_ext_context *in,
+				unsigned char *out)
+{
+	/* No extended flag set, no point to compress the context */
+	if (!(flags & TBICTX_XEXT_BIT))
+		return -EINVAL;
+
+	/* No need to copy XEXT struct, as it's already in place */
+	memcpy(out, &(in->regs.ctx), sizeof(TBIEXTCTX));
+	out += sizeof(TBIEXTCTX);
+
+	/* Copy the TBICTX_XDX8 registers */
+	if (flags & TBICTX_XDX8_BIT) {
+		memcpy(out, &(in->regs.bb8), sizeof(TBICTXEXTBB8));
+		out += sizeof(TBICTXEXTBB8);
+	}
+
+	/* Ax.4 -> Ax.7 context */
+	if (flags & TBICTX_XAXX_BIT) {
+		memcpy(out, &(in->regs.ax[0]), TBICTXEXTAXX_BYTES);
+		out += TBICTXEXTAXX_BYTES;
+	}
+
+	/* Hardware loop */
+	if (flags & TBICTX_XHL2_BIT) {
+		memcpy(out, &(in->regs.hl2), sizeof(TBICTXEXTHL2));
+		out += sizeof(TBICTXEXTHL2);
+	}
+
+	/* Per-thread DSP registers */
+	if (flags & TBICTX_XTDP_BIT) {
+		memcpy(out, &(in->regs.ext), sizeof(TBICTXEXTTDPR));
+		out += sizeof(TBICTXEXTTDPR);
+	}
+
+	return 0;
+}
+
+static int metag_ext_ctx_set(struct task_struct *target,
+		  const struct user_regset *regset,
+		  unsigned int pos, unsigned int count,
+		  const void *kbuf, const void __user *ubuf)
+{
+	struct meta_ext_context *dsp_ctx = NULL;
+	const unsigned int dsp_size = sizeof(struct meta_ext_context);
+	unsigned int flags;
+	int ret = 0;
+
+	if (!target->thread.dsp_context) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* get a temporary context made */
+	dsp_ctx = kzalloc(dsp_size, GFP_KERNEL);
+	if (!dsp_ctx) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	if (kbuf) {
+		const unsigned long *k = kbuf;
+		memcpy(dsp_ctx, k, dsp_size);
+	} else {
+		const unsigned long  __user *u = ubuf;
+		if (!access_ok(VERIFY_READ, u, dsp_size)) {
+			ret = -EFAULT;
+			goto err;
+		}
+		copy_from_user(dsp_ctx, u, dsp_size);
+	}
+
+	/* Compress into the thread extended context */
+	flags = (target->thread.user_flags >> 16);
+	compress_ext_context(flags, dsp_ctx,
+			(unsigned char *)target->thread.dsp_context);
+
+err:
+	kfree(dsp_ctx);
+
+done:
+	return ret;
+}
+#endif
+
+static const struct user_regset metag_regsets[] = {
+	[REGSET_GENERAL] = {
+		.core_note_type = NT_PRSTATUS,
+		.n = sizeof(struct pt_regs) / sizeof(long),
+		.size = sizeof(long),
+		.align = sizeof(long),
+		.get = metag_regs_get,
+		.set = metag_regs_set,
+	},
+#ifdef CONFIG_META_DSP
+	[REGSET_EXT] = {
+		.core_note_type = NT_PRSTATUS,
+		.n = sizeof(struct meta_ext_context) / sizeof(long),
+		.size = sizeof(long),
+		.align = sizeof(long),
+		.get = metag_ext_ctx_get,
+		.set = metag_ext_ctx_set,
+	},
+#endif
+};
+
+static const struct user_regset_view user_metag_view = {
+	.name = "metag",
+	.e_machine = EM_METAG,
+	.regsets = metag_regsets,
+	.n = ARRAY_SIZE(metag_regsets)
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+	return &user_metag_view;
+}
+
+long arch_ptrace(struct task_struct *child, long request, unsigned long addr,
+		 unsigned long data)
+{
+	int ret;
+	unsigned long __user *datap = (void __user *)data;
+
+	switch (request) {
+	/*
+	 * read the word at location addr in the USER area.
+	 */
+	case PTRACE_PEEKUSR:
+		ret = ptrace_read_user(child, addr, datap);
+		break;
+
+	/*
+	 * write the word at location addr in the USER area.
+	 */
+	case PTRACE_POKEUSR:
+		ret = ptrace_write_user(child, addr, data);
+		break;
+
+	case PTRACE_PEEK_BP:
+		ret = ptrace_read_bp(child, addr, datap);
+		break;
+
+	case PTRACE_POKE_BP:
+		ret = ptrace_write_bp(child, addr, data);
+		break;
+
+	case PTRACE_GETREGS:	/* Get all gp regs from the child. */
+		return copy_regset_to_user(child, &user_metag_view,
+					   REGSET_GENERAL,
+					   0, sizeof(struct pt_regs),
+					   (void __user *)datap);
+
+	case PTRACE_SETREGS:	/* Set all gp regs in the child. */
+		return copy_regset_from_user(child, &user_metag_view,
+					     REGSET_GENERAL,
+					     0, sizeof(struct pt_regs),
+					     (const void __user *)datap);
+
+#ifdef CONFIG_META_DSP
+	case PTRACE_GETEXTREGS:	/* Get all extended context regs */
+		return copy_regset_to_user(child, &user_metag_view,
+				REGSET_EXT, 0, sizeof(struct meta_ext_context),
+				(void __user *)datap);
+		break;
+
+	case PTRACE_SETEXTREGS:	/* Set all gp regs in the child. */
+		return copy_regset_from_user(child, &user_metag_view,
+				REGSET_EXT, 0, sizeof(struct meta_ext_context),
+				(const void __user *)datap);
+		break;
+#endif
+
+	default:
+		ret = ptrace_request(child, request, addr, data);
+		break;
+	}
+
+	return ret;
+}
+
+int syscall_trace_enter(struct pt_regs *regs)
+{
+	int ret = 0;
+
+	if (test_thread_flag(TIF_SYSCALL_TRACE))
+		ret = tracehook_report_syscall_entry(regs);
+
+	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+		trace_sys_enter(regs, regs->ctx.DX[0].U1);
+
+	return ret ? 0 : regs->ctx.DX[0].U1;
+}
+
+void syscall_trace_leave(struct pt_regs *regs)
+{
+	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+		trace_sys_exit(regs, regs->ctx.DX[0].U1);
+
+	if (test_thread_flag(TIF_SYSCALL_TRACE))
+		tracehook_report_syscall_exit(regs, 0);
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 15/40] metag: Time keeping
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (11 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 14/40] metag: Ptrace James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 16/40] metag: Traps James Hogan
                   ` (27 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add time keeping code for metag. Meta hardware threads have 2 timers.
The background timer (TXTIMER) is used as a free-running time base, and
the interrupt timer (TXTIMERI) is used for the timer interrupt. Both
counters traditionally count at approximately 1MHz.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/clock.h     |   51 +++++++++++
 arch/metag/include/asm/delay.h     |   29 ++++++
 arch/metag/include/asm/mach/arch.h |    4 +
 arch/metag/kernel/clock.c          |   53 ++++++++++++
 arch/metag/kernel/time.c           |  165 ++++++++++++++++++++++++++++++++++++
 5 files changed, 302 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/clock.h
 create mode 100644 arch/metag/include/asm/delay.h
 create mode 100644 arch/metag/kernel/clock.c
 create mode 100644 arch/metag/kernel/time.c

diff --git a/arch/metag/include/asm/clock.h b/arch/metag/include/asm/clock.h
new file mode 100644
index 0000000..3e2915a
--- /dev/null
+++ b/arch/metag/include/asm/clock.h
@@ -0,0 +1,51 @@
+/*
+ * arch/metag/include/asm/clock.h
+ *
+ * Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _METAG_CLOCK_H_
+#define _METAG_CLOCK_H_
+
+#include <asm/mach/arch.h>
+
+/**
+ * struct meta_clock_desc - Meta Core clock callbacks.
+ * @get_core_freq:	Get the frequency of the Meta core. If this is NULL, the
+ *			core frequency will be determined like this:
+ *			Meta 1: based on loops_per_jiffy.
+ *			Meta 2: (EXPAND_TIMER_DIV + 1) MHz.
+ */
+struct meta_clock_desc {
+	unsigned long		(*get_core_freq)(void);
+};
+
+extern struct meta_clock_desc _meta_clock;
+
+/*
+ * Set up the default clock, ensuring all callbacks are valid - only accessible
+ * during boot.
+ */
+void setup_meta_clocks(struct meta_clock_desc *desc);
+
+/**
+ * get_coreclock() - Get the frequency of the Meta core clock.
+ *
+ * Returns:	The Meta core clock frequency in Hz.
+ */
+static inline unsigned long get_coreclock(void)
+{
+	/*
+	 * Use the current clock callback. If set correctly this will provide
+	 * the most accurate frequency as it can be calculated directly from the
+	 * PLL configuration. otherwise a default callback will have been set
+	 * instead.
+	 */
+	return _meta_clock.get_core_freq();
+}
+
+#endif /* _METAG_CLOCK_H_ */
diff --git a/arch/metag/include/asm/delay.h b/arch/metag/include/asm/delay.h
new file mode 100644
index 0000000..9c92f99
--- /dev/null
+++ b/arch/metag/include/asm/delay.h
@@ -0,0 +1,29 @@
+#ifndef _METAG_DELAY_H
+#define _METAG_DELAY_H
+
+/*
+ * Copyright (C) 1993 Linus Torvalds
+ *
+ * Delay routines calling functions in arch/metag/lib/delay.c
+ */
+
+/* Undefined functions to get compile-time errors */
+extern void __bad_udelay(void);
+extern void __bad_ndelay(void);
+
+extern void __udelay(unsigned long usecs);
+extern void __ndelay(unsigned long nsecs);
+extern void __const_udelay(unsigned long xloops);
+extern void __delay(unsigned long loops);
+
+/* 0x10c7 is 2**32 / 1000000 (rounded up) */
+#define udelay(n) (__builtin_constant_p(n) ? \
+	((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c7ul)) : \
+	__udelay(n))
+
+/* 0x5 is 2**32 / 1000000000 (rounded up) */
+#define ndelay(n) (__builtin_constant_p(n) ? \
+	((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \
+	__ndelay(n))
+
+#endif /* _METAG_DELAY_H */
diff --git a/arch/metag/include/asm/mach/arch.h b/arch/metag/include/asm/mach/arch.h
index 8429c76..230d896 100644
--- a/arch/metag/include/asm/mach/arch.h
+++ b/arch/metag/include/asm/mach/arch.h
@@ -16,10 +16,13 @@
 
 #include <linux/stddef.h>
 
+#include <asm/clock.h>
+
 /**
  * struct machine_desc - Describes a board controlled by a Meta.
  * @name:		Board/SoC name.
  * @dt_compat:		Array of device tree 'compatible' strings.
+ * @clocks:		Clock callbacks.
  *
  * @init_early:		Early init callback.
  * @init_irq:		IRQ init callback for setting up IRQ controllers.
@@ -34,6 +37,7 @@
 struct machine_desc {
 	const char		*name;
 	const char		**dt_compat;
+	struct meta_clock_desc	*clocks;
 
 	void			(*init_early)(void);
 	void			(*init_irq)(void);
diff --git a/arch/metag/kernel/clock.c b/arch/metag/kernel/clock.c
new file mode 100644
index 0000000..089287e
--- /dev/null
+++ b/arch/metag/kernel/clock.c
@@ -0,0 +1,53 @@
+/*
+ * arch/metag/kernel/clock.c
+ *
+ * Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/param.h>
+#include <asm/clock.h>
+
+struct meta_clock_desc _meta_clock;
+
+/* Default machine get_core_freq callback. */
+static unsigned long get_core_freq_default(void)
+{
+#ifdef CONFIG_META21
+	/*
+	 * Meta 2 cores divide down the core clock for the Meta timers, so we
+	 * can estimate the core clock from the divider.
+	 */
+	return (readl(EXPAND_TIMER_DIV) + 1) * 1000000;
+#else
+	/*
+	 * On Meta 1 we don't know the core clock, but assuming the Meta timer
+	 * is correct it can be estimated based on loops_per_jiffy.
+	 */
+	return (loops_per_jiffy * HZ * 5) >> 1;
+#endif
+}
+
+/**
+ * setup_meta_clocks() - Set up the Meta clock.
+ * @desc:	Clock descriptor usually provided by machine description
+ *
+ * Ensures all callbacks are valid.
+ */
+void __init setup_meta_clocks(struct meta_clock_desc *desc)
+{
+	/* copy callbacks */
+	if (desc)
+		_meta_clock = *desc;
+
+	/* set fallback functions */
+	if (!_meta_clock.get_core_freq)
+		_meta_clock.get_core_freq = get_core_freq_default;
+}
+
diff --git a/arch/metag/kernel/time.c b/arch/metag/kernel/time.c
new file mode 100644
index 0000000..3e20b74
--- /dev/null
+++ b/arch/metag/kernel/time.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 Imagination Technologies Ltd.
+ *
+ * This file contains the Meta-specific time handling details.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+
+#include <asm/clock.h>
+#define METAG_ALL_VALUES
+#define METAC_ALL_VALUES
+#include <asm/tbx/machine.inc>
+#include <asm/tbx/metagtbx.h>
+#include <asm/hwthread.h>
+#include <asm/core_reg.h>
+
+#define HARDWARE_FREQ		1000000	/* 1MHz */
+#define HARDWARE_DIV		1	/* divide by 1 = 1MHz clock */
+#define HARDWARE_TO_NS_SHIFT	10	/* convert ticks to ns */
+
+static unsigned int hwtimer_freq = HARDWARE_FREQ;
+static DEFINE_PER_CPU(struct clock_event_device, local_clockevent);
+static DEFINE_PER_CPU(char [11], local_clockevent_name);
+
+static int metag_timer_set_next_event(unsigned long delta,
+				      struct clock_event_device *dev)
+{
+	TBI_SETREG(TXTIMERI, -delta);
+	return 0;
+}
+
+static void metag_timer_set_mode(enum clock_event_mode mode,
+				 struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_RESUME:
+		break;
+
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		/* We should disable the IRQ here */
+		break;
+
+	case CLOCK_EVT_MODE_PERIODIC:
+	case CLOCK_EVT_MODE_UNUSED:
+		WARN_ON(1);
+		break;
+	};
+}
+
+static inline cycle_t txtimer_read(void)
+{
+	unsigned int ret;
+
+	__asm__ volatile ("MOV	%0, TXTIMER\n" : "=r" (ret));
+	return ret;
+}
+
+static cycle_t metag_clocksource_read(struct clocksource *cs)
+{
+	return txtimer_read();
+}
+
+static struct clocksource clocksource_metag = {
+	.name = "META",
+	.rating = 200,
+	.mask = CLOCKSOURCE_MASK(32),
+	.read = metag_clocksource_read,
+	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static irqreturn_t metag_timer_interrupt(int irq, void *dummy)
+{
+	struct clock_event_device *evt = &__get_cpu_var(local_clockevent);
+
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction metag_timer_irq = {
+	.name = "META core timer",
+	.handler = metag_timer_interrupt,
+	.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_PERCPU,
+};
+
+unsigned long long sched_clock(void)
+{
+	unsigned long long ticks = txtimer_read();
+	return ticks << HARDWARE_TO_NS_SHIFT;
+}
+
+void __cpuinit local_timer_setup(unsigned int cpu)
+{
+	unsigned int txdivtime;
+	struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
+	char *name = per_cpu(local_clockevent_name, cpu);
+
+	txdivtime = TBI_GETREG(TXDIVTIME);
+
+	txdivtime &= ~TXDIVTIME_DIV_BITS;
+	txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS);
+
+	TBI_SETREG(TXDIVTIME, txdivtime);
+
+	sprintf(name, "META %d", cpu);
+	clk->name = name;
+	clk->features = CLOCK_EVT_FEAT_ONESHOT,
+
+	clk->rating = 200,
+	clk->shift = 12,
+	clk->irq = TBID_SIGNUM_TRT,
+	clk->set_mode = metag_timer_set_mode,
+	clk->set_next_event = metag_timer_set_next_event,
+
+	clk->mult = div_sc(hwtimer_freq, NSEC_PER_SEC, clk->shift);
+	clk->max_delta_ns = clockevent_delta2ns(0x7fffffff, clk);
+	clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
+	clk->cpumask = cpumask_of(cpu);
+
+	clockevents_register_device(clk);
+
+	/*
+	 * For all non-boot CPUs we need to synchronize our free
+	 * running clock (TXTIMER) with the boot CPU's clock.
+	 *
+	 * While this won't be accurate, it should be close enough.
+	 */
+	if (cpu) {
+		unsigned int thread0 = cpu_2_hwthread_id[0];
+		unsigned long val;
+
+		val = core_reg_read(TXUCT_ID, TXTIMER_REGNUM, thread0);
+
+		asm volatile("MOV TXTIMER, %0\n" : : "r" (val));
+	}
+}
+
+void __init time_init(void)
+{
+	/*
+	 * On Meta 2 SoCs, the actual frequency of the timer is based on the
+	 * Meta core clock speed divided by an integer, so it is only
+	 * approximately 1MHz. Calculating the real frequency here drastically
+	 * reduces clock skew on these SoCs.
+	 */
+#ifdef CONFIG_META21
+	hwtimer_freq = get_coreclock() / (readl(EXPAND_TIMER_DIV) + 1);
+#endif
+	clocksource_register_hz(&clocksource_metag, hwtimer_freq);
+
+	setup_irq(TBID_SIGNUM_TRT, &metag_timer_irq);
+
+	local_timer_setup(smp_processor_id());
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 16/40] metag: Traps
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (12 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 15/40] metag: Time keeping James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 17/40] metag: IRQ handling James Hogan
                   ` (26 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add trap code for metag. At the lowest level Meta traps (and return from
interrupt instruction - RTI) simply swap the PC and PCX registers and
optionally toggle the interrupt status bit (ISTAT). Low level TBX code
in tbipcx.S handles the core context save, determine the TBX signal
number based on the core trigger that fired (using the TXSTATI status
register), and and call TBX signal handlers (mostly in traps.c) via a
vector table.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/switch.h |   22 +
 arch/metag/include/asm/traps.h  |   46 ++
 arch/metag/kernel/kick.c        |   94 ++++
 arch/metag/kernel/tbiunexp.S    |   22 +
 arch/metag/kernel/traps.c       |  967 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 1151 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/switch.h
 create mode 100644 arch/metag/include/asm/traps.h
 create mode 100644 arch/metag/kernel/kick.c
 create mode 100644 arch/metag/kernel/tbiunexp.S
 create mode 100644 arch/metag/kernel/traps.c

diff --git a/arch/metag/include/asm/switch.h b/arch/metag/include/asm/switch.h
new file mode 100644
index 0000000..53eed0d
--- /dev/null
+++ b/arch/metag/include/asm/switch.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+#ifndef _ASM_METAG_SWITCH_H
+#define _ASM_METAG_SWITCH_H
+
+/* metag SWITCH codes */
+#define __METAG_SW_IDLE		0x400000	/* idle waiting for trigger */
+#define __METAG_SW_PERM_BREAK	0x400002	/* compiled in breakpoint */
+#define __METAG_SW_SYS_LEGACY	0x440000	/* legacy system calls */
+#define __METAG_SW_SYS		0x440001	/* system calls */
+
+/* metag SWITCH instruction encoding */
+#define __METAG_SW_ENCODING(TYPE)	(0xaf000000 | (__METAG_SW_##TYPE))
+
+#endif /* _ASM_METAG_SWITCH_H */
diff --git a/arch/metag/include/asm/traps.h b/arch/metag/include/asm/traps.h
new file mode 100644
index 0000000..6c79cc5
--- /dev/null
+++ b/arch/metag/include/asm/traps.h
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (C) 2005,2008 Imagination Technologies
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _METAG_TBIVECTORS_H
+#define _METAG_TBIVECTORS_H
+
+#ifndef __ASSEMBLY__
+
+#define METAC_ALL_VALUES
+#define METAG_ALL_VALUES
+#include <asm/tbx/machine.inc>
+#include <asm/tbx/metagtbx.h>
+
+typedef TBIRES (*kick_irq_func_t)(TBIRES, int, int, int, PTBI, int *);
+
+extern TBIRES kick_handler(TBIRES, int, int, int, PTBI);
+struct kick_irq_handler {
+	struct list_head list;
+	kick_irq_func_t func;
+};
+
+extern void kick_register_func(struct kick_irq_handler *);
+extern void kick_unregister_func(struct kick_irq_handler *);
+
+extern void head_end(TBIRES, unsigned long);
+extern TBIRES tail_end(TBIRES, unsigned long);
+
+DECLARE_PER_CPU(PTBI, pTBI);
+extern PTBI pTBI_get(unsigned int);
+
+extern int ret_from_fork(TBIRES arg);
+
+extern int do_page_fault(struct pt_regs *regs, unsigned long address,
+			 unsigned int write_access, unsigned int trapno);
+
+extern TBIRES __TBIUnExpXXX(TBIRES State, int SigNum, int Triggers, int Inst,
+			    PTBI pTBI);
+
+#endif
+
+#endif /* _METAG_TBIVECTORS_H */
diff --git a/arch/metag/kernel/kick.c b/arch/metag/kernel/kick.c
new file mode 100644
index 0000000..c9858e2
--- /dev/null
+++ b/arch/metag/kernel/kick.c
@@ -0,0 +1,94 @@
+/*
+ *  Copyright (C) 2009 Imagination Technologies
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * The Meta KICK interrupt mechanism is generally a useful feature, so
+ * we provide an interface for registering multiple interrupt
+ * handlers. All the registered interrupt handlers are "chained". When
+ * a KICK interrupt is received the first function in the list is
+ * called. If that interrupt handler cannot handle the KICK the next
+ * one is called, then the next until someone handles it (or we run
+ * out of functions). As soon as one function handles the interrupt no
+ * other handlers are called.
+ *
+ * The only downside of chaining interrupt handlers is that each
+ * handler must be able to detect whether the KICK was intended for it
+ * or not.  For example, when the IPI handler runs and it sees that
+ * there are no IPI messages it must not signal that the KICK was
+ * handled, thereby giving the other handlers a chance to run.
+ *
+ * The reason that we provide our own interface for calling KICK
+ * handlers instead of using the generic kernel infrastructure is that
+ * the KICK handlers require access to a CPU's pTBI structure. So we
+ * pass it as an argument.
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+
+#include <asm/traps.h>
+
+/*
+ * All accesses/manipulations of kick_handlers_list should be
+ * performed while holding kick_handlers_lock.
+ */
+static DEFINE_SPINLOCK(kick_handlers_lock);
+static LIST_HEAD(kick_handlers_list);
+
+void kick_register_func(struct kick_irq_handler *kh)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&kick_handlers_lock, flags);
+
+	list_add_tail(&kh->list, &kick_handlers_list);
+
+	spin_unlock_irqrestore(&kick_handlers_lock, flags);
+}
+
+void kick_unregister_func(struct kick_irq_handler *kh)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&kick_handlers_lock, flags);
+
+	list_del(&kh->list);
+
+	spin_unlock_irqrestore(&kick_handlers_lock, flags);
+}
+
+TBIRES
+kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI)
+{
+	struct kick_irq_handler *kh;
+	struct list_head *lh;
+	int handled = 0;
+	TBIRES ret;
+
+	head_end(State, ~INTS_OFF_MASK);
+
+	trace_hardirqs_off();
+
+	/*
+	 * There is no need to disable interrupts here because we
+	 * can't nest KICK interrupts in a KICK interrupt handler.
+	 */
+	spin_lock(&kick_handlers_lock);
+
+	list_for_each(lh, &kick_handlers_list) {
+		kh = list_entry(lh, struct kick_irq_handler, list);
+
+		ret = kh->func(State, SigNum, Triggers, Inst, pTBI, &handled);
+		if (handled)
+			break;
+	}
+
+	spin_unlock(&kick_handlers_lock);
+
+	WARN_ON(!handled);
+
+	return tail_end(ret, 0);
+}
diff --git a/arch/metag/kernel/tbiunexp.S b/arch/metag/kernel/tbiunexp.S
new file mode 100644
index 0000000..6ee8419
--- /dev/null
+++ b/arch/metag/kernel/tbiunexp.S
@@ -0,0 +1,22 @@
+/* Pass a breakpoint through to Codescape */
+
+#include <asm/tbx/metagtbx.h>
+
+	.text
+        .global	___TBIUnExpXXX
+        .type   ___TBIUnExpXXX,function
+___TBIUnExpXXX:
+	TSTT	D0Ar2,#TBICTX_CRIT_BIT	! Result of nestable int call?
+	BZ	$LTBINormCase		! UnExpXXX at background level
+	MOV	D0Re0,TXMASKI		! Read TXMASKI
+	XOR	TXMASKI,D1Re0,D1Re0	! Turn off BGNDHALT handling!
+	OR	D0Ar2,D0Ar2,D0Re0	! Preserve bits cleared
+$LTBINormCase:
+	MSETL 	[A0StP],D0Ar6,D0Ar4,D0Ar2	! Save args on stack
+	SETL 	[A0StP++],D0Ar2,D1Ar1	! Init area for returned values
+	SWITCH 	#0xC20208		! Total stack frame size 8 Dwords
+					!            write back size 2 Dwords
+	GETL 	D0Re0,D1Re0,[--A0StP]	! Get result
+	SUB 	A0StP,A0StP,#(8*3)	! Recover stack frame
+	MOV 	PC,D1RtP
+        .size   	___TBIUnExpXXX,.-___TBIUnExpXXX
diff --git a/arch/metag/kernel/traps.c b/arch/metag/kernel/traps.c
new file mode 100644
index 0000000..d72f93a
--- /dev/null
+++ b/arch/metag/kernel/traps.c
@@ -0,0 +1,967 @@
+/*
+ *  Meta exception handling.
+ *
+ *  Copyright (C) 2005,2006,2007,2008,2009,2012 Imagination Technologies Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/kdebug.h>
+#include <linux/kexec.h>
+#include <linux/unistd.h>
+#include <linux/smp.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+
+#include <asm/bug.h>
+#include <asm/irqflags.h>
+#include <asm/siginfo.h>
+#include <asm/traps.h>
+#include <asm/hwthread.h>
+#include <asm/switch.h>
+#include <asm/user_gateway.h>
+#include <asm/syscall.h>
+#include <asm/syscalls.h>
+
+/* Passing syscall arguments as long long is quicker. */
+typedef unsigned int (*LPSYSCALL) (unsigned long long,
+				   unsigned long long,
+				   unsigned long long,
+				   unsigned int /*pt_regs */);
+
+/*
+ * Users of LNKSET should compare the bus error bits obtained from DEFR
+ * against TXDEFR_LNKSET_SUCCESS only as the failure code will vary between
+ * different cores revisions.
+ */
+#define TXDEFR_LNKSET_SUCCESS 0x02000000
+#define TXDEFR_LNKSET_FAILURE 0x04000000
+
+/*
+ * Our global TBI handle.  Initialised from setup.c/setup_arch.
+ */
+DECLARE_PER_CPU(PTBI, pTBI);
+
+#ifdef CONFIG_SMP
+static DEFINE_PER_CPU(unsigned int, trigger_mask);
+#else
+unsigned int global_trigger_mask;
+#endif
+
+unsigned long per_cpu__stack_save[NR_CPUS];
+
+static const char * const trap_names[] = {
+	[TBIXXF_SIGNUM_IIF] = "Illegal instruction fault",
+	[TBIXXF_SIGNUM_PGF] = "Privilege violation",
+	[TBIXXF_SIGNUM_DHF] = "Unaligned data access fault",
+	[TBIXXF_SIGNUM_IGF] = "Code fetch general read failure",
+	[TBIXXF_SIGNUM_DGF] = "Data access general read/write fault",
+	[TBIXXF_SIGNUM_IPF] = "Code fetch page fault",
+	[TBIXXF_SIGNUM_DPF] = "Data access page fault",
+	[TBIXXF_SIGNUM_IHF] = "Instruction breakpoint",
+	[TBIXXF_SIGNUM_DWF] = "Read-only data access fault",
+};
+
+const char *trap_name(int trapno)
+{
+	if (trapno >= 0 && trapno < ARRAY_SIZE(trap_names)
+			&& trap_names[trapno])
+		return trap_names[trapno];
+	return "Unknown fault";
+}
+
+static DEFINE_SPINLOCK(die_lock);
+
+void die(const char *str, struct pt_regs *regs, long err,
+	 unsigned long addr)
+{
+	static int die_counter;
+
+	oops_enter();
+
+	spin_lock_irq(&die_lock);
+	console_verbose();
+	bust_spinlocks(1);
+	pr_err("%s: err %04lx (%s) addr %08lx [#%d]\n", str, err & 0xffff,
+	       trap_name(err & 0xffff), addr, ++die_counter);
+
+	print_modules();
+	show_regs(regs);
+
+	pr_err("Process: %s (pid: %d, stack limit = %p)\n", current->comm,
+	       task_pid_nr(current), task_stack_page(current) + THREAD_SIZE);
+
+	bust_spinlocks(0);
+	add_taint(TAINT_DIE);
+	if (kexec_should_crash(current))
+		crash_kexec(regs);
+
+	if (in_interrupt())
+		panic("Fatal exception in interrupt");
+
+	if (panic_on_oops)
+		panic("Fatal exception");
+
+	spin_unlock_irq(&die_lock);
+	oops_exit();
+	do_exit(SIGSEGV);
+}
+
+#ifdef CONFIG_META_DSP
+/*
+ * The ECH encoding specifies the size of a DSPRAM as,
+ *
+ *		"slots" / 4
+ *
+ * A "slot" is the size of two DSPRAM bank entries; an entry from
+ * DSPRAM bank A and an entry from DSPRAM bank B. One DSPRAM bank
+ * entry is 4 bytes.
+ */
+#define SLOT_SZ	8
+static inline unsigned int decode_dspram_size(unsigned int size)
+{
+	unsigned int _sz = size & 0x7f;
+
+	return _sz * SLOT_SZ * 4;
+}
+
+static void dspram_save(struct meta_ext_context *dsp_ctx,
+			unsigned int ramA_sz, unsigned int ramB_sz)
+{
+	unsigned int ram_sz[2];
+	int i;
+
+	ram_sz[0] = ramA_sz;
+	ram_sz[1] = ramB_sz;
+
+	for (i = 0; i < 2; i++) {
+		if (ram_sz[i] != 0) {
+			unsigned int sz;
+
+			if (i == 0)
+				sz = decode_dspram_size(ram_sz[i] >> 8);
+			else
+				sz = decode_dspram_size(ram_sz[i]);
+
+			if (dsp_ctx->ram[i] == NULL) {
+				dsp_ctx->ram[i] = kmalloc(sz, GFP_KERNEL);
+
+				if (dsp_ctx->ram[i] == NULL)
+					panic("couldn't save DSP context");
+			} else {
+				if (ram_sz[i] > dsp_ctx->ram_sz[i]) {
+					kfree(dsp_ctx->ram[i]);
+
+					dsp_ctx->ram[i] = kmalloc(sz,
+								  GFP_KERNEL);
+
+					if (dsp_ctx->ram[i] == NULL)
+						panic("couldn't save DSP context");
+				}
+			}
+
+			if (i == 0)
+				__TBIDspramSaveA(ram_sz[i], dsp_ctx->ram[i]);
+			else
+				__TBIDspramSaveB(ram_sz[i], dsp_ctx->ram[i]);
+
+			dsp_ctx->ram_sz[i] = ram_sz[i];
+		}
+	}
+}
+#endif /* CONFIG_META_DSP */
+
+/*
+ * Allow interrupts to be nested and save any "extended" register
+ * context state, e.g. DSP regs and RAMs.
+ */
+static void nest_interrupts(TBIRES State, unsigned long mask)
+{
+#ifdef CONFIG_META_DSP
+	struct meta_ext_context *dsp_ctx;
+	unsigned int D0_8;
+
+	/*
+	 * D0.8 may contain an ECH encoding. The upper 16 bits
+	 * tell us what DSP resources the current process is
+	 * using. OR the bits into the SaveMask so that
+	 * __TBINestInts() knows what resources to save as
+	 * part of this context.
+	 *
+	 * Don't save the context if we're nesting interrupts in the
+	 * kernel because the kernel doesn't use DSP hardware.
+	 */
+	D0_8 = TBI_GETREG(D0.8);
+
+	if (D0_8 && (State.Sig.SaveMask & TBICTX_PRIV_BIT)) {
+		State.Sig.SaveMask |= (D0_8 >> 16);
+
+		dsp_ctx = current->thread.dsp_context;
+		if (dsp_ctx == NULL) {
+			dsp_ctx = kzalloc(sizeof(*dsp_ctx), GFP_KERNEL);
+			if (dsp_ctx == NULL)
+				panic("couldn't save DSP context: ENOMEM");
+
+			current->thread.dsp_context = dsp_ctx;
+		}
+
+		current->thread.user_flags |= (D0_8 & 0xffff0000);
+		__TBINestInts(State, &dsp_ctx->regs, mask);
+		dspram_save(dsp_ctx, D0_8 & 0x7f00, D0_8 & 0x007f);
+	} else
+		__TBINestInts(State, NULL, mask);
+#else
+	__TBINestInts(State, NULL, mask);
+#endif
+}
+
+void head_end(TBIRES State, unsigned long mask)
+{
+	unsigned int savemask = (unsigned short)State.Sig.SaveMask;
+	unsigned int ctx_savemask = (unsigned short)State.Sig.pCtx->SaveMask;
+
+	if (savemask & TBICTX_PRIV_BIT) {
+		ctx_savemask |= TBICTX_PRIV_BIT;
+		current->thread.user_flags = savemask;
+	}
+
+	/* Always undo the sleep bit */
+	ctx_savemask &= ~TBICTX_WAIT_BIT;
+
+	/* Always save the catch buffer and RD pipe if they are dirty */
+	savemask |= TBICTX_XCBF_BIT;
+
+	/* Only save the catch and RD if we have not already done so.
+	 * Note - the RD bits are in the pCtx only, and not in the
+	 * State.SaveMask.
+	 */
+	if ((savemask & TBICTX_CBUF_BIT) ||
+	    (ctx_savemask & TBICTX_CBRP_BIT)) {
+		/* Have we already saved the buffers though?
+		 * - See TestTrack 5071 */
+		if (ctx_savemask & TBICTX_XCBF_BIT) {
+			/* Strip off the bits so the call to __TBINestInts
+			 * won't save the buffers again. */
+			savemask &= ~TBICTX_CBUF_BIT;
+			ctx_savemask &= ~TBICTX_CBRP_BIT;
+		}
+	}
+
+#ifdef CONFIG_META21
+	{
+		unsigned int depth, txdefr;
+
+		/*
+		 * Save TXDEFR state.
+		 *
+		 * All interrupts are disabled at this point so we
+		 * don't need to perform any locking. We must do this
+		 * dance before we use LNKGET or LNKSET.
+		 */
+		BUG_ON(current->thread.int_depth > HARDIRQ_BITS);
+
+		depth = current->thread.int_depth++;
+
+		__asm__ __volatile__("MOV %0, TXDEFR" : "=r" (txdefr));
+
+		txdefr &= TXDEFR_BUS_STATE_BITS;
+		if (txdefr & TXDEFR_LNKSET_SUCCESS)
+			current->thread.txdefr_failure &= ~(1 << depth);
+		else
+			current->thread.txdefr_failure |= (1 << depth);
+	}
+#endif
+
+	State.Sig.SaveMask = savemask;
+	State.Sig.pCtx->SaveMask = ctx_savemask;
+
+	nest_interrupts(State, mask);
+
+#ifdef CONFIG_POISON_CATCH_BUFFERS
+	/* Poison the catch registers.  This shows up any mistakes we have
+	 * made in their handling MUCH quicker.
+	 */
+	TBI_SETREG(TXCATCH0, 0x87650021);
+	TBI_SETREG(TXCATCH1, 0x87654322);
+	TBI_SETREG(TXCATCH2, 0x87654323);
+	TBI_SETREG(TXCATCH3, 0x87654324);
+#endif /* CONFIG_POISON_CATCH_BUFFERS */
+}
+
+TBIRES tail_end(TBIRES State, unsigned long orig_syscall)
+{
+	struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx;
+	unsigned long flags;
+
+	if (user_mode(regs)) {
+		local_irq_enable();
+		/* This is actually a crucial little line - if the process
+		 * needs swapping out, then this is where it happens!
+		 */
+		if (need_resched())
+			schedule();
+
+		flags = current_thread_info()->flags;
+		if (flags & (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME)) {
+			/* Note the passing in of the original syscall number.
+			 * This is used for implementing signal restart.
+			 */
+			do_notify_resume(regs, orig_syscall != 0,
+					 orig_syscall, flags);
+		}
+#ifdef CONFIG_META_FPU
+		if (current->thread.fpu_context &&
+		    current->thread.fpu_context->needs_restore) {
+			__TBICtxFPURestore(State, current->thread.fpu_context);
+			/*
+			 * Clearing this bit ensures the FP unit is not made
+			 * active again unless it is used.
+			 */
+			State.Sig.SaveMask &= ~TBICTX_FPAC_BIT;
+			current->thread.fpu_context->needs_restore = false;
+		}
+		State.Sig.TrigMask |= TBI_TRIG_BIT(TBID_SIGNUM_DFR);
+#endif
+	}
+
+	local_irq_disable();
+
+	/* TBI will turn interrupts back on at some point. */
+	if (!irqs_disabled_flags((unsigned long)State.Sig.TrigMask))
+		trace_hardirqs_on();
+
+#ifdef CONFIG_META_DSP
+	/*
+	 * If we previously saved an extended context then restore it
+	 * now. Otherwise, clear D0.8 because this process is not
+	 * using DSP hardware.
+	 */
+	if (State.Sig.pCtx->SaveMask & TBICTX_XEXT_BIT) {
+		unsigned int D0_8;
+		struct meta_ext_context *dsp_ctx = current->thread.dsp_context;
+
+		/* Make sure we're going to return to userland. */
+		BUG_ON(current->thread.int_depth != 1);
+
+		if (dsp_ctx->ram_sz[0] > 0)
+			__TBIDspramRestoreA(dsp_ctx->ram_sz[0],
+					    dsp_ctx->ram[0]);
+		if (dsp_ctx->ram_sz[1] > 0)
+			__TBIDspramRestoreB(dsp_ctx->ram_sz[1],
+					    dsp_ctx->ram[1]);
+
+		State.Sig.SaveMask |= State.Sig.pCtx->SaveMask;
+		__TBICtxRestore(State, current->thread.dsp_context);
+		D0_8 = TBI_GETREG(D0.8);
+		D0_8 |= current->thread.user_flags & 0xffff0000;
+		D0_8 |= (dsp_ctx->ram_sz[1] | dsp_ctx->ram_sz[0]) & 0xffff;
+		TBI_SETREG(D0.8, D0_8);
+	} else
+		TBI_SETREG(D0.8, 0);
+#endif /* CONFIG_META_DSP */
+
+#ifdef CONFIG_META21
+	{
+		unsigned int depth, txdefr;
+
+		depth = --current->thread.int_depth;
+
+		BUG_ON(user_mode(regs) && depth);
+
+		__asm__ __volatile__("MOV %0, TXDEFR\n" : "=r" (txdefr));
+
+		txdefr &= ~TXDEFR_BUS_STATE_BITS;
+
+		/* Do we need to restore a failure code into TXDEFR? */
+		if (current->thread.txdefr_failure & (1 << depth))
+			txdefr |= (TXDEFR_LNKSET_FAILURE | TXDEFR_BUS_TRIG_BIT);
+		else
+			txdefr |= (TXDEFR_LNKSET_SUCCESS | TXDEFR_BUS_TRIG_BIT);
+
+		__asm__ __volatile__("MOV TXDEFR, %0\n" : : "r" (txdefr));
+	}
+#endif
+	return State;
+}
+
+#ifdef CONFIG_SMP
+/*
+ * If we took an interrupt in the middle of __kuser_get_tls then we need
+ * to rewind the PC to the start of the function in case the process
+ * gets migrated to another thread (SMP only) and it reads the wrong tls
+ * data.
+ */
+static void restart_critical_section(TBIRES State)
+{
+	unsigned long get_tls_start;
+	unsigned long get_tls_end;
+
+	get_tls_start = (unsigned long)__kuser_get_tls -
+		(unsigned long)&__user_gateway_start;
+
+	get_tls_start += USER_GATEWAY_PAGE;
+
+	get_tls_end = (unsigned long)__kuser_get_tls_end -
+		(unsigned long)&__user_gateway_start;
+
+	get_tls_end += USER_GATEWAY_PAGE;
+
+	if ((State.Sig.pCtx->CurrPC >= get_tls_start) &&
+	    (State.Sig.pCtx->CurrPC < get_tls_end))
+		State.Sig.pCtx->CurrPC = get_tls_start;
+}
+#else
+/*
+ * If we took an interrupt in the middle of
+ * __kuser_cmpxchg then we need to rewind the PC to the
+ * start of the function.
+ */
+static void restart_critical_section(TBIRES State)
+{
+	unsigned long cmpxchg_start;
+	unsigned long cmpxchg_end;
+
+	cmpxchg_start = (unsigned long)__kuser_cmpxchg -
+		(unsigned long)&__user_gateway_start;
+
+	cmpxchg_start += USER_GATEWAY_PAGE;
+
+	cmpxchg_end = (unsigned long)__kuser_cmpxchg_end -
+		(unsigned long)&__user_gateway_start;
+
+	cmpxchg_end += USER_GATEWAY_PAGE;
+
+	if ((State.Sig.pCtx->CurrPC >= cmpxchg_start) &&
+	    (State.Sig.pCtx->CurrPC < cmpxchg_end))
+		State.Sig.pCtx->CurrPC = cmpxchg_start;
+}
+#endif
+
+TBIRES trigger_handler(TBIRES State, int SigNum, int Triggers, int Inst,
+		       PTBI pTBI)
+{
+	head_end(State, ~INTS_OFF_MASK);
+
+	/* If we interrupted user code handle any critical sections. */
+	if (State.Sig.SaveMask & TBICTX_PRIV_BIT)
+		restart_critical_section(State);
+
+	trace_hardirqs_off();
+
+	do_IRQ(SigNum, (struct pt_regs *)State.Sig.pCtx);
+
+	return tail_end(State, 0);
+}
+
+static unsigned int load_fault(PTBICTXEXTCB0 pbuf)
+{
+	return pbuf->CBFlags & TXCATCH0_READ_BIT;
+}
+
+static unsigned long fault_address(PTBICTXEXTCB0 pbuf)
+{
+	return pbuf->CBAddr;
+}
+
+static void unhandled_fault(struct pt_regs *regs, unsigned long addr,
+			    int signo, int code, int trapno)
+{
+	if (user_mode(regs)) {
+		siginfo_t info;
+
+		if (show_unhandled_signals && unhandled_signal(current, signo)
+		    && printk_ratelimit()) {
+
+			pr_info("pid %d unhandled fault: pc 0x%08x, addr 0x%08lx, trap %d (%s)\n",
+				current->pid, regs->ctx.CurrPC, addr,
+				trapno, trap_name(trapno));
+			print_vma_addr(" in ", regs->ctx.CurrPC);
+			print_vma_addr(" rtp in ", regs->ctx.DX[4].U1);
+			printk("\n");
+			show_regs(regs);
+		}
+
+		info.si_signo = signo;
+		info.si_errno = 0;
+		info.si_code = code;
+		info.si_addr = (__force void __user *)addr;
+		info.si_trapno = trapno;
+		force_sig_info(signo, &info, current);
+	} else {
+		die("Oops", regs, trapno, addr);
+	}
+}
+
+static int handle_data_fault(PTBICTXEXTCB0 pcbuf, struct pt_regs *regs,
+			     unsigned int data_address, int trapno)
+{
+	int ret;
+
+	ret = do_page_fault(regs, data_address, !load_fault(pcbuf), trapno);
+
+	return ret;
+}
+
+static unsigned long get_inst_fault_address(struct pt_regs *regs)
+{
+	return regs->ctx.CurrPC;
+}
+
+TBIRES fault_handler(TBIRES State, int SigNum, int Triggers,
+		     int Inst, PTBI pTBI)
+{
+	struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx;
+	PTBICTXEXTCB0 pcbuf = (PTBICTXEXTCB0)&regs->extcb0;
+	unsigned long data_address;
+
+	head_end(State, ~INTS_OFF_MASK);
+
+	/* Hardware breakpoint or data watch */
+	if ((SigNum == TBIXXF_SIGNUM_IHF) ||
+	    ((SigNum == TBIXXF_SIGNUM_DHF) &&
+	     (pcbuf[0].CBFlags & (TXCATCH0_WATCH1_BIT |
+				  TXCATCH0_WATCH0_BIT)))) {
+		if (current->thread.hwbp_context && user_mode(regs))
+			force_sig(SIGTRAP, current);
+		else
+			State = __TBIUnExpXXX(State, SigNum, Triggers, Inst,
+					      pTBI);
+		return tail_end(State, 0);
+	}
+
+	local_irq_enable();
+
+	data_address = fault_address(pcbuf);
+
+	switch (SigNum) {
+	case TBIXXF_SIGNUM_IGF:
+		/* 1st-level entry invalid (instruction fetch) */
+	case TBIXXF_SIGNUM_IPF: {
+		/* 2nd-level entry invalid (instruction fetch) */
+		unsigned long addr = get_inst_fault_address(regs);
+		do_page_fault(regs, addr, 0, SigNum);
+		break;
+	}
+
+	case TBIXXF_SIGNUM_DGF:
+		/* 1st-level entry invalid (data access) */
+	case TBIXXF_SIGNUM_DPF:
+		/* 2nd-level entry invalid (data access) */
+	case TBIXXF_SIGNUM_DWF:
+		/* Write to read only page */
+		handle_data_fault(pcbuf, regs, data_address, SigNum);
+		break;
+
+	case TBIXXF_SIGNUM_IIF:
+		/* Illegal instruction */
+		unhandled_fault(regs, regs->ctx.CurrPC, SIGILL, ILL_ILLOPC,
+				SigNum);
+		break;
+
+	case TBIXXF_SIGNUM_DHF:
+		/* Unaligned access */
+		unhandled_fault(regs, data_address, SIGBUS, BUS_ADRALN,
+				SigNum);
+		break;
+	case TBIXXF_SIGNUM_PGF:
+		/* Privilege violation */
+		unhandled_fault(regs, data_address, SIGSEGV, SEGV_ACCERR,
+				SigNum);
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	return tail_end(State, 0);
+}
+
+static bool switch_is_syscall(unsigned int inst)
+{
+	return inst == __METAG_SW_ENCODING(SYS);
+}
+
+static bool switch_is_legacy_syscall(unsigned int inst)
+{
+	return inst == __METAG_SW_ENCODING(SYS_LEGACY);
+}
+
+static bool switch_is_idle(unsigned int inst)
+{
+	return inst == __METAG_SW_ENCODING(IDLE);
+}
+
+static inline void step_over_switch(struct pt_regs *regs, unsigned int inst)
+{
+	regs->ctx.CurrPC += 4;
+}
+
+static inline int test_syscall_work(void)
+{
+	return current_thread_info()->flags & _TIF_WORK_SYSCALL_MASK;
+}
+
+TBIRES switch1_handler(TBIRES State, int SigNum, int Triggers,
+		       int Inst, PTBI pTBI)
+{
+	struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx;
+	unsigned int sysnumber;
+	unsigned long long a1_a2, a3_a4, a5_a6, a7;
+	LPSYSCALL syscall_entry;
+
+	head_end(State, ~INTS_OFF_MASK);
+
+	/*
+	 * If this is not a syscall SWITCH it could be a breakpoint or a
+	 * low power request (system syscall).
+	 */
+	if (!switch_is_syscall(Inst)) {
+		if (switch_is_idle(Inst) && !user_mode(regs)) {
+			regs->ctx.SaveMask |= TBICTX_WAIT_BIT;
+			step_over_switch(regs, Inst);
+		} else {
+			/*
+			 * Alert the user if they're trying to use legacy system
+			 * calls. This suggests they need to update their C
+			 * library and build against up to date kernel headers.
+			 */
+			if (switch_is_legacy_syscall(Inst))
+				pr_warn_once("WARNING: A legacy syscall was made. Your userland needs updating.\n");
+			/*
+			 * We don't know how to handle the SWITCH and cannot
+			 * safely ignore it, so treat all unknown switches
+			 * (including breakpoints) as traps.
+			 */
+			force_sig(SIGTRAP, current);
+		}
+		return tail_end(State, 0);
+	}
+
+	local_irq_enable();
+
+	sysnumber = regs->ctx.DX[0].U1;
+
+	if (test_syscall_work())
+		sysnumber = syscall_trace_enter(regs);
+
+	/* Skip over the SWITCH instruction - or you just get 'stuck' on it! */
+	step_over_switch(regs, Inst);
+
+	if (sysnumber >= __NR_syscalls) {
+		pr_debug("unknown syscall number: %d\n", sysnumber);
+		syscall_entry = (LPSYSCALL) sys_ni_syscall;
+	} else {
+		syscall_entry = (LPSYSCALL) sys_call_table[sysnumber];
+	}
+
+	/* arg7 is always the registers on Meta. */
+	a7 = (unsigned int)regs;
+
+	/* Use 64bit loads for speed. */
+	a5_a6 = *(unsigned long long *)&regs->ctx.DX[1];
+	a3_a4 = *(unsigned long long *)&regs->ctx.DX[2];
+	a1_a2 = *(unsigned long long *)&regs->ctx.DX[3];
+
+	/* here is the actual call to the syscall handler functions */
+	regs->ctx.DX[0].U0 = syscall_entry(a1_a2, a3_a4, a5_a6, a7);
+
+	if (test_syscall_work())
+		syscall_trace_leave(regs);
+
+	return tail_end(State, sysnumber);
+}
+
+TBIRES switchx_handler(TBIRES State, int SigNum, int Triggers,
+		       int Inst, PTBI pTBI)
+{
+	struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx;
+
+	/*
+	 * This can be caused by any user process simply executing an unusual
+	 * SWITCH instruction. If there's no DA, __TBIUnExpXXX will cause the
+	 * thread to stop, so signal a SIGTRAP instead.
+	 */
+	head_end(State, ~INTS_OFF_MASK);
+	if (user_mode(regs))
+		force_sig(SIGTRAP, current);
+	else
+		State = __TBIUnExpXXX(State, SigNum, Triggers, Inst, pTBI);
+	return tail_end(State, 0);
+}
+
+#ifdef CONFIG_META21
+TBIRES fpe_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI)
+{
+	struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx;
+	unsigned int error_state = Triggers;
+	siginfo_t info;
+
+	head_end(State, ~INTS_OFF_MASK);
+
+	local_irq_enable();
+
+	info.si_signo = SIGFPE;
+
+	if (error_state & TXSTAT_FPE_INVALID_BIT)
+		info.si_code = FPE_FLTINV;
+	else if (error_state & TXSTAT_FPE_DIVBYZERO_BIT)
+		info.si_code = FPE_FLTDIV;
+	else if (error_state & TXSTAT_FPE_OVERFLOW_BIT)
+		info.si_code = FPE_FLTOVF;
+	else if (error_state & TXSTAT_FPE_UNDERFLOW_BIT)
+		info.si_code = FPE_FLTUND;
+	else if (error_state & TXSTAT_FPE_INEXACT_BIT)
+		info.si_code = FPE_FLTRES;
+	else
+		info.si_code = 0;
+	info.si_errno = 0;
+	info.si_addr = (__force void __user *)regs->ctx.CurrPC;
+	force_sig_info(SIGFPE, &info, current);
+
+	return tail_end(State, 0);
+}
+#endif
+
+#ifdef CONFIG_METAG_SUSPEND_MEM
+struct traps_context {
+	PTBIAPIFN fnSigs[TBID_SIGNUM_MAX + 1];
+};
+
+static struct traps_context *metag_traps_context;
+
+int traps_save_context(void)
+{
+	unsigned long cpu = smp_processor_id();
+	PTBI _pTBI = per_cpu(pTBI, cpu);
+	struct traps_context *context;
+
+	context = kzalloc(sizeof(*context), GFP_ATOMIC);
+	if (!context)
+		return -ENOMEM;
+
+	memcpy(context->fnSigs, (void *)_pTBI->fnSigs, sizeof(context->fnSigs));
+
+	metag_traps_context = context;
+	return 0;
+}
+
+int traps_restore_context(void)
+{
+	unsigned long cpu = smp_processor_id();
+	PTBI _pTBI = per_cpu(pTBI, cpu);
+	struct traps_context *context = metag_traps_context;
+
+	metag_traps_context = NULL;
+
+	memcpy((void *)_pTBI->fnSigs, context->fnSigs, sizeof(context->fnSigs));
+
+	kfree(context);
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_SMP
+unsigned int get_trigger_mask(void)
+{
+	unsigned long cpu = smp_processor_id();
+	return per_cpu(trigger_mask, cpu);
+}
+
+static void set_trigger_mask(unsigned int mask)
+{
+	unsigned long cpu = smp_processor_id();
+	per_cpu(trigger_mask, cpu) = mask;
+}
+#else
+static void set_trigger_mask(unsigned int mask)
+{
+	global_trigger_mask = mask;
+}
+#endif
+
+void __cpuinit per_cpu_trap_init(unsigned long cpu)
+{
+	TBIRES int_context;
+	unsigned int thread = cpu_2_hwthread_id[cpu];
+
+	set_trigger_mask(TBI_INTS_INIT(thread) | /* interrupts */
+			 TBI_TRIG_BIT(TBID_SIGNUM_LWK) | /* low level kick */
+			 TBI_TRIG_BIT(TBID_SIGNUM_SW1) |
+			 TBI_TRIG_BIT(TBID_SIGNUM_SWS));
+
+	/* non-priv - use current stack */
+	int_context.Sig.pCtx = NULL;
+	/* Start with interrupts off */
+	int_context.Sig.TrigMask = INTS_OFF_MASK;
+	int_context.Sig.SaveMask = 0;
+
+	/* And call __TBIASyncTrigger() */
+	__TBIASyncTrigger(int_context);
+}
+
+void __init trap_init(void)
+{
+	unsigned long cpu = smp_processor_id();
+	PTBI _pTBI = per_cpu(pTBI, cpu);
+
+	_pTBI->fnSigs[TBID_SIGNUM_XXF] = fault_handler;
+	_pTBI->fnSigs[TBID_SIGNUM_SW0] = switchx_handler;
+	_pTBI->fnSigs[TBID_SIGNUM_SW1] = switch1_handler;
+	_pTBI->fnSigs[TBID_SIGNUM_SW2] = switchx_handler;
+	_pTBI->fnSigs[TBID_SIGNUM_SW3] = switchx_handler;
+	_pTBI->fnSigs[TBID_SIGNUM_SWK] = kick_handler;
+
+#ifdef CONFIG_META21
+	_pTBI->fnSigs[TBID_SIGNUM_DFR] = __TBIHandleDFR;
+	_pTBI->fnSigs[TBID_SIGNUM_FPE] = fpe_handler;
+#endif
+
+	per_cpu_trap_init(cpu);
+}
+
+void tbi_startup_interrupt(int irq)
+{
+	unsigned long cpu = smp_processor_id();
+	PTBI _pTBI = per_cpu(pTBI, cpu);
+
+	BUG_ON(irq > TBID_SIGNUM_MAX);
+
+	/* For TR1 and TR2, the thread id is encoded in the irq number */
+	if (irq >= TBID_SIGNUM_T10 && irq < TBID_SIGNUM_TR3)
+		cpu = hwthread_id_2_cpu[(irq - TBID_SIGNUM_T10) % 4];
+
+	set_trigger_mask(get_trigger_mask() | TBI_TRIG_BIT(irq));
+
+	_pTBI->fnSigs[irq] = trigger_handler;
+}
+
+void tbi_shutdown_interrupt(int irq)
+{
+	unsigned long cpu = smp_processor_id();
+	PTBI _pTBI = per_cpu(pTBI, cpu);
+
+	BUG_ON(irq > TBID_SIGNUM_MAX);
+
+	set_trigger_mask(get_trigger_mask() & ~TBI_TRIG_BIT(irq));
+
+	_pTBI->fnSigs[irq] = __TBIUnExpXXX;
+}
+
+int ret_from_fork(TBIRES arg)
+{
+	struct task_struct *prev = arg.Switch.pPara;
+	struct task_struct *tsk = current;
+	struct pt_regs *regs = task_pt_regs(tsk);
+	TBIRES Next;
+
+	schedule_tail(prev);
+
+	if (test_syscall_work())
+		syscall_trace_leave(regs);
+
+	preempt_disable();
+
+	Next.Sig.TrigMask = get_trigger_mask();
+	Next.Sig.SaveMask = 0;
+	Next.Sig.pCtx = &regs->ctx;
+
+	set_gateway_tls(current->thread.tls_ptr);
+
+	preempt_enable_no_resched();
+
+	/* And interrupts should come back on when we resume the real usermode
+	 * code. Call __TBIASyncResume()
+	 */
+	__TBIASyncResume(tail_end(Next, 0));
+	/* ASyncResume should NEVER return */
+	BUG();
+	return 0;
+}
+
+void show_trace(struct task_struct *tsk, unsigned long *sp,
+		struct pt_regs *regs)
+{
+	unsigned long addr;
+#ifdef CONFIG_FRAME_POINTER
+	unsigned long fp, fpnew;
+	unsigned long stack;
+#endif
+
+	if (regs && user_mode(regs))
+		return;
+
+	printk("\nCall trace: ");
+#ifdef CONFIG_KALLSYMS
+	printk("\n");
+#endif
+
+	if (!tsk)
+		tsk = current;
+
+#ifdef CONFIG_FRAME_POINTER
+	if (regs) {
+		print_ip_sym(regs->ctx.CurrPC);
+		fp = regs->ctx.AX[1].U0;
+	} else {
+		__asm__ __volatile__("MOV %0, A0FrP\n" : "=da" (fp));
+	}
+
+	/* detect when the frame pointer has been used for other purposes and
+	 * doesn't point to the stack (it may point completely elsewhere which
+	 * kstack_end may not detect).
+	 */
+	stack = (unsigned long)task_stack_page(tsk);
+	while (fp >= stack && fp + 8 <= stack + THREAD_SIZE) {
+		addr = __raw_readl((unsigned long *)(fp + 4)) - 4;
+		if (kernel_text_address(addr))
+			print_ip_sym(addr);
+		else
+			break;
+		/* stack grows up, so frame pointers must decrease */
+		fpnew = __raw_readl((unsigned long *)(fp + 0));
+		if (fpnew >= fp)
+			break;
+		fp = fpnew;
+	}
+#else
+	while (!kstack_end(sp)) {
+		addr = (*sp--) - 4;
+		if (kernel_text_address(addr))
+			print_ip_sym(addr);
+	}
+#endif
+
+	printk("\n");
+
+	debug_show_held_locks(tsk);
+}
+
+void show_stack(struct task_struct *tsk, unsigned long *sp)
+{
+	if (!tsk)
+		tsk = current;
+	if (tsk == current)
+		sp = (unsigned long *)current_stack_pointer;
+	else
+		sp = (unsigned long *)tsk->thread.kernel_context->AX[0].U0;
+
+	show_trace(tsk, sp, NULL);
+}
+
+void dump_stack(void)
+{
+	show_stack(NULL, NULL);
+}
+EXPORT_SYMBOL(dump_stack);
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 17/40] metag: IRQ handling
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (13 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 16/40] metag: Traps James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-11-09 14:12   ` Arnd Bergmann
  2012-10-31 16:13 ` [RFC PATCH v1 18/40] metag: System Calls James Hogan
                   ` (25 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add core IRQ handling for metag. The code in irq.c exposes the TBX
signal numbers as Linux IRQs.

Meta core internal interrupts (from HWSTATMETA and friends) are vectored
onto the TR1 core trigger for the current thread. This is demultiplexed
in irq_internal.c to individual Linux IRQs for each internal interrupt.

External SoC interrupts (from HWSTATEXT and friends) are vectored onto
the TR2 core trigger for the current thread. This is demultiplexed in
irq_external.c to individual Linux IRQs for each external SoC interrupt.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 .../devicetree/bindings/metag/meta-intc.txt        |   92 ++
 arch/metag/include/asm/irq.h                       |   79 ++
 arch/metag/include/asm/irq_external.h              |   33 +
 arch/metag/include/asm/irq_internal.h              |   16 +
 arch/metag/include/asm/irqflags.h                  |  100 +++
 arch/metag/kernel/irq.c                            |  277 ++++++
 arch/metag/kernel/irq_external.c                   |  886 ++++++++++++++++++++
 arch/metag/kernel/irq_internal.c                   |  235 ++++++
 8 files changed, 1718 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/metag/meta-intc.txt
 create mode 100644 arch/metag/include/asm/irq.h
 create mode 100644 arch/metag/include/asm/irq_external.h
 create mode 100644 arch/metag/include/asm/irq_internal.h
 create mode 100644 arch/metag/include/asm/irqflags.h
 create mode 100644 arch/metag/kernel/irq.c
 create mode 100644 arch/metag/kernel/irq_external.c
 create mode 100644 arch/metag/kernel/irq_internal.c

diff --git a/Documentation/devicetree/bindings/metag/meta-intc.txt b/Documentation/devicetree/bindings/metag/meta-intc.txt
new file mode 100644
index 0000000..b9bbe19
--- /dev/null
+++ b/Documentation/devicetree/bindings/metag/meta-intc.txt
@@ -0,0 +1,92 @@
+* Meta External Trigger Controller Binding
+
+This binding specifies what properties must be available in the device tree
+representation of a Meta external trigger controller.
+
+Required properties:
+
+    - compatible: Specifies the compatibility list for the interrupt controller.
+      The type shall be <string> and the value shall include "img,meta-intc".
+
+    - num-banks: Specifies the number of interrupt banks (each of which can
+      handle 32 interrupt sources).
+
+    - interrupt-controller: The presence of this property identifies the node
+      as an interupt controller. No property value shall be defined.
+
+    - #interrupt-cells: Specifies the number of cells needed to encode an
+      interrupt source. The type shall be a <u32> and the value shall be 2.
+
+    - #address-cells: Specifies the number of cells needed to encode an
+      address. The type shall be <u32> and the value shall be 0. As such,
+      'interrupt-map' nodes do not have to specify a parent unit address.
+
+Optional properties:
+
+    - no-mask: The controller doesn't have any mask registers.
+
+    - default-level: List of default level bitfields, one for each bank, where
+      each set bit indicates that the interrupt should be level sensitive by
+      default rather than edge. The purpose of this is mainly to ease the
+      transition to device tree from platform data. It should not be necessary
+      if all IRQ users are configured with devicetree as each one should set the
+      flow type in the interrupt specifier.
+
+* Interrupt Specifier Definition
+
+  Interrupt specifiers consists of 2 cells encoded as follows:
+
+    - <1st-cell>: The interrupt-number that identifies the interrupt source.
+
+    - <2nd-cell>: The Linux interrupt flags containing level-sense information,
+                  encoded as follows:
+                    1 = edge triggered
+                    4 = level-sensitive
+
+* Examples
+
+Example 1:
+
+	/*
+	 * Meta external trigger block
+	 */
+	intc: intc {
+		// This is an interrupt controller node.
+		interrupt-controller;
+
+		// No address cells so that 'interrupt-map' nodes which
+		// reference this interrupt controller node do not need a parent
+		// address specifier.
+		#address-cells = <0>;
+
+		// Two cells to encode interrupt sources.
+		#interrupt-cells = <2>;
+
+		// Number of interrupt banks
+		num-banks = <2>;
+
+		// IRQs 0-35 are level sensitive, except 16 and 34
+		default-level = <0xfffeffff 0xb>;
+
+		// No HWMASKEXT is available (specify on Chorus2 and Comet ES1)
+		no-mask;
+
+		// Compatible with Meta hardware trigger block.
+		compatible = "img,meta-intc";
+	};
+
+Example 2:
+
+	/*
+	 * An interrupt generating device that is wired to a Meta external
+	 * trigger block.
+	 */
+	uart1: uart@0x02004c00 {
+		// Interrupt source '5' that is level-sensitive.
+		// Note that there are only two cells as specified in the
+		// interrupt parent's '#interrupt-cells' property.
+		interrupts = <5 4 /* level */>;
+
+		// The interrupt controller that this device is wired to.
+		interrupt-parent = <&intc>;
+	};
diff --git a/arch/metag/include/asm/irq.h b/arch/metag/include/asm/irq.h
new file mode 100644
index 0000000..da24886
--- /dev/null
+++ b/arch/metag/include/asm/irq.h
@@ -0,0 +1,79 @@
+#ifndef __ASM_METAG_IRQ_H
+#define __ASM_METAG_IRQ_H
+
+#define HWSTATEXT_OFFSET_MAX	0
+#define HWSTATEXT2_OFFSET_MAX	0
+#define HWSTATEXT4_OFFSET_MAX	0
+#define HWSTATEXT6_OFFSET_MAX	0
+#define init_soc_IRQ NULL
+#include <asm/irq_internal.h>
+#ifndef HW_IRQS
+#define HW_IRQS  (HWSTATMETA_OFFSET_MAX + HWSTATEXT_OFFSET_MAX + \
+		HWSTATEXT2_OFFSET_MAX + HWSTATEXT4_OFFSET_MAX + \
+		HWSTATEXT6_OFFSET_MAX)
+#endif
+
+#define META_IRQS 32
+
+#define NR_IRQS (META_IRQS + HW_IRQS)
+
+/*
+ * Automatic IRQ numbering
+ *
+ * Maps triggers into IRQs depending on which triggers are used.
+ * HWSTATEXT*_OFFSET_MAX must be defined in SoC's irq.h file.
+ */
+#define HWSTATMETA_TO_IRQ(offset)	(META_IRQS+(offset))
+
+#define HWSTATEXT_TO_HWIRQ(offset)	(offset)
+#define HWSTATEXT2_TO_HWIRQ(offset)	(HWSTATEXT_TO_HWIRQ(HWSTATEXT_OFFSET_MAX)+(offset))
+#define HWSTATEXT4_TO_HWIRQ(offset)	(HWSTATEXT2_TO_HWIRQ(HWSTATEXT2_OFFSET_MAX)+(offset))
+#define HWSTATEXT6_TO_HWIRQ(offset)	(HWSTATEXT4_TO_HWIRQ(HWSTATEXT4_OFFSET_MAX)+(offset))
+
+#define HWSTATEXT_TO_IRQ(offset)	(HWSTATMETA_TO_IRQ(HWSTATMETA_OFFSET_MAX)+(offset))
+#define HWSTATEXT2_TO_IRQ(offset)	(HWSTATEXT_TO_IRQ(HWSTATEXT_OFFSET_MAX)+(offset))
+#define HWSTATEXT4_TO_IRQ(offset)	(HWSTATEXT2_TO_IRQ(HWSTATEXT2_OFFSET_MAX)+(offset))
+#define HWSTATEXT6_TO_IRQ(offset)	(HWSTATEXT4_TO_IRQ(HWSTATEXT4_OFFSET_MAX)+(offset))
+
+static inline unsigned int IRQ_TO_OFFSET(unsigned int irq)
+{
+	if (irq < HWSTATMETA_TO_IRQ(0))
+		return irq;
+	else if (irq < HWSTATEXT_TO_IRQ(0))
+		return irq - HWSTATMETA_TO_IRQ(0);
+	else if (irq < HWSTATEXT2_TO_IRQ(0))
+		return irq - HWSTATEXT_TO_IRQ(0);
+	else if (irq < HWSTATEXT4_TO_IRQ(0))
+		return irq - HWSTATEXT2_TO_IRQ(0);
+	else /* if (irq < HWSTATEXT6_TO_IRQ(0)) */
+		return irq - HWSTATEXT6_TO_IRQ(0);
+}
+
+#ifdef CONFIG_4KSTACKS
+extern void irq_ctx_init(int cpu);
+extern void irq_ctx_exit(int cpu);
+# define __ARCH_HAS_DO_SOFTIRQ
+#else
+# define irq_ctx_init(cpu) do { } while (0)
+# define irq_ctx_exit(cpu) do { } while (0)
+#endif
+
+void tbi_startup_interrupt(int);
+void tbi_shutdown_interrupt(int);
+
+struct pt_regs;
+
+extern void do_IRQ(int irq, struct pt_regs *regs);
+
+#ifdef CONFIG_METAG_SUSPEND_MEM
+int traps_save_context(void);
+int traps_restore_context(void);
+#endif
+
+#include <asm-generic/irq.h>
+
+#ifdef CONFIG_HOTPLUG_CPU
+extern void migrate_irqs(void);
+#endif
+
+#endif /* __ASM_METAG_IRQ_H */
diff --git a/arch/metag/include/asm/irq_external.h b/arch/metag/include/asm/irq_external.h
new file mode 100644
index 0000000..77ebc2a
--- /dev/null
+++ b/arch/metag/include/asm/irq_external.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 Imagination Technologies
+ */
+
+#ifndef _METAG_IRQ_EXTERNAL_H_
+#define _METAG_IRQ_EXTERNAL_H_
+
+struct irq_data;
+struct platform_device;
+
+/* called from core irq code at init */
+int init_external_IRQ(void);
+
+/*
+ * called from SoC init_irq() callback to dynamically indicate the lack of
+ * HWMASKEXT registers.
+ */
+void meta_intc_no_mask(void);
+
+/*
+ * These allow SoCs to specialise the interrupt controller from their init_irq
+ * callbacks.
+ */
+
+extern struct irq_chip meta_intc_edge_chip;
+extern struct irq_chip meta_intc_level_chip;
+
+/* this should be called in the mask callback */
+void meta_intc_mask_irq_simple(struct irq_data *data);
+/* this should be called in the unmask callback */
+void meta_intc_unmask_irq_simple(struct irq_data *data);
+
+#endif /* _METAG_IRQ_EXTERNAL_H_ */
diff --git a/arch/metag/include/asm/irq_internal.h b/arch/metag/include/asm/irq_internal.h
new file mode 100644
index 0000000..be35f74
--- /dev/null
+++ b/arch/metag/include/asm/irq_internal.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2011 Imagination Technologies
+ */
+
+#ifndef _METAG_IRQ_INTERNAL_H_
+#define _METAG_IRQ_INTERNAL_H_
+
+#define HWSTATMETA_OFFSET_MAX	32
+
+#ifdef CONFIG_META_PERFCOUNTER_IRQS
+extern void init_internal_IRQ(void);
+#else
+#define init_internal_IRQ() do {} while (0)
+#endif
+
+#endif /* _METAG_IRQ_INTERNAL_H_ */
diff --git a/arch/metag/include/asm/irqflags.h b/arch/metag/include/asm/irqflags.h
new file mode 100644
index 0000000..52936b0
--- /dev/null
+++ b/arch/metag/include/asm/irqflags.h
@@ -0,0 +1,100 @@
+/*
+ * IRQ flags handling
+ *
+ * This file gets included from lowlevel asm headers too, to provide
+ * wrapped versions of the local_irq_*() APIs, based on the
+ * raw_local_irq_*() functions from the lowlevel headers.
+ */
+#ifndef _ASM_IRQFLAGS_H
+#define _ASM_IRQFLAGS_H
+
+#ifndef __ASSEMBLY__
+
+#define METAC_ALL_VALUES
+#define METAG_ALL_VALUES
+#include <asm/tbx/metagtbx.h>
+#include <asm/tbx/machine.inc>
+
+#define INTS_OFF_MASK TXSTATI_BGNDHALT_BIT
+
+#ifdef CONFIG_SMP
+extern unsigned int get_trigger_mask(void);
+#else
+
+extern unsigned int global_trigger_mask;
+
+static inline unsigned int get_trigger_mask(void)
+{
+	return global_trigger_mask;
+}
+#endif
+
+static inline unsigned long arch_local_save_flags(void)
+{
+	unsigned long flags;
+
+	asm volatile("MOV %0,TXMASKI\n" : "=r" (flags));
+
+	return flags;
+}
+
+static inline int arch_irqs_disabled_flags(unsigned long flags)
+{
+	return (flags & ~INTS_OFF_MASK) == 0;
+}
+
+static inline int arch_irqs_disabled(void)
+{
+	unsigned long flags = arch_local_save_flags();
+
+	return arch_irqs_disabled_flags(flags);
+}
+
+static inline unsigned long __irqs_disabled(void)
+{
+	/*
+	 * We shouldn't enable exceptions if they are not already
+	 * enabled. This is required for chancalls to work correctly.
+	 */
+	return arch_local_save_flags() & INTS_OFF_MASK;
+}
+
+/*
+ * For spinlocks, etc:
+ */
+static inline unsigned long arch_local_irq_save(void)
+{
+	unsigned long flags = __irqs_disabled();
+
+	asm volatile("SWAP %0,TXMASKI\n" : "=r" (flags) : "0" (flags)
+		     : "memory");
+
+	return flags;
+}
+
+static inline void arch_local_irq_restore(unsigned long flags)
+{
+	asm volatile("MOV TXMASKI,%0\n" : : "r" (flags) : "memory");
+}
+
+static inline void arch_local_irq_disable(void)
+{
+	unsigned long flags = __irqs_disabled();
+
+	asm volatile("MOV TXMASKI,%0\n" : : "r" (flags) : "memory");
+}
+
+static inline void arch_local_irq_enable(void)
+{
+#ifdef CONFIG_SMP
+	preempt_disable();
+	arch_local_irq_restore(get_trigger_mask());
+	preempt_enable_no_resched();
+#else
+	arch_local_irq_restore(get_trigger_mask());
+#endif
+}
+
+#endif /* (__ASSEMBLY__) */
+
+#endif /* !(_ASM_IRQFLAGS_H) */
diff --git a/arch/metag/kernel/irq.c b/arch/metag/kernel/irq.c
new file mode 100644
index 0000000..195fb12
--- /dev/null
+++ b/arch/metag/kernel/irq.c
@@ -0,0 +1,277 @@
+/*
+ * Linux/Meta general interrupt handling code
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/ratelimit.h>
+
+#include <asm/irq_external.h>
+#include <asm/mach/arch.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_4KSTACKS
+union irq_ctx {
+	struct thread_info      tinfo;
+	u32                     stack[THREAD_SIZE/sizeof(u32)];
+};
+
+static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly;
+static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly;
+#endif
+
+static unsigned int startup_meta_irq(struct irq_data *data)
+{
+	tbi_startup_interrupt(data->irq);
+	return 0;
+}
+
+static void shutdown_meta_irq(struct irq_data *data)
+{
+	tbi_shutdown_interrupt(data->irq);
+}
+
+void do_IRQ(int irq, struct pt_regs *regs)
+{
+	struct pt_regs *old_regs = set_irq_regs(regs);
+#ifdef CONFIG_4KSTACKS
+	struct irq_desc *desc;
+	union irq_ctx *curctx, *irqctx;
+	u32 *isp;
+#endif
+
+	irq_enter();
+
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+	/* Debugging check for stack overflow: is there less than 1KB free? */
+	{
+		unsigned long sp;
+
+		__asm__ __volatile__("MOV %0,A0StP\n" : "=r"(sp));
+		sp &= THREAD_SIZE - 1;
+
+		if (unlikely(sp > (THREAD_SIZE - 1024)))
+			pr_err("Stack overflow in do_IRQ: %ld\n", sp);
+	}
+#endif
+
+
+#ifdef CONFIG_4KSTACKS
+	curctx = (union irq_ctx *) current_thread_info();
+	irqctx = hardirq_ctx[smp_processor_id()];
+
+	/*
+	 * this is where we switch to the IRQ stack. However, if we are
+	 * already using the IRQ stack (because we interrupted a hardirq
+	 * handler) we can't do that and just have to keep using the
+	 * current stack (which is the irq stack already after all)
+	 */
+	if (curctx != irqctx) {
+		/* build the stack frame on the IRQ stack */
+		isp = (u32 *) ((char *)irqctx + sizeof(struct thread_info));
+		irqctx->tinfo.task = curctx->tinfo.task;
+
+		/*
+		 * Copy the softirq bits in preempt_count so that the
+		 * softirq checks work in the hardirq context.
+		 */
+		irqctx->tinfo.preempt_count =
+			(irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |
+			(curctx->tinfo.preempt_count & SOFTIRQ_MASK);
+
+		desc = irq_to_desc(irq);
+
+		asm volatile(
+			"MOV   D0.5,%0\n"
+			"MOV   D1Ar1,%1\n"
+			"MOV   D1RtP,%2\n"
+			"MOV   D0Ar2,%3\n"
+			"SWAP  A0StP,D0.5\n"
+			"SWAP  PC,D1RtP\n"
+			"MOV   A0StP,D0.5\n"
+			:
+			: "r" (isp), "r" (irq), "r" (desc->handle_irq),
+			  "r" (desc)
+			: "memory", "cc", "D1Ar1", "D0Ar2", "D1Ar3", "D0Ar4",
+			  "D1Ar5", "D0Ar6", "D0Re0", "D1Re0", "D0.4", "D1RtP",
+			  "D0.5"
+			);
+	} else
+#endif
+		generic_handle_irq(irq);
+
+	irq_exit();
+
+	set_irq_regs(old_regs);
+}
+
+#ifdef CONFIG_4KSTACKS
+
+static char softirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss;
+
+static char hardirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss;
+
+/*
+ * allocate per-cpu stacks for hardirq and for softirq processing
+ */
+void irq_ctx_init(int cpu)
+{
+	union irq_ctx *irqctx;
+
+	if (hardirq_ctx[cpu])
+		return;
+
+	irqctx = (union irq_ctx *) &hardirq_stack[cpu * THREAD_SIZE];
+	irqctx->tinfo.task              = NULL;
+	irqctx->tinfo.exec_domain       = NULL;
+	irqctx->tinfo.cpu               = cpu;
+	irqctx->tinfo.preempt_count     = HARDIRQ_OFFSET;
+	irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);
+
+	hardirq_ctx[cpu] = irqctx;
+
+	irqctx = (union irq_ctx *) &softirq_stack[cpu * THREAD_SIZE];
+	irqctx->tinfo.task              = NULL;
+	irqctx->tinfo.exec_domain       = NULL;
+	irqctx->tinfo.cpu               = cpu;
+	irqctx->tinfo.preempt_count     = 0;
+	irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);
+
+	softirq_ctx[cpu] = irqctx;
+
+	pr_info("CPU %u irqstacks, hard=%p soft=%p\n",
+		cpu, hardirq_ctx[cpu], softirq_ctx[cpu]);
+}
+
+void irq_ctx_exit(int cpu)
+{
+	hardirq_ctx[smp_processor_id()] = NULL;
+}
+
+extern asmlinkage void __do_softirq(void);
+
+asmlinkage void do_softirq(void)
+{
+	unsigned long flags;
+	struct thread_info *curctx;
+	union irq_ctx *irqctx;
+	u32 *isp;
+
+	if (in_interrupt())
+		return;
+
+	local_irq_save(flags);
+
+	if (local_softirq_pending()) {
+		curctx = current_thread_info();
+		irqctx = softirq_ctx[smp_processor_id()];
+		irqctx->tinfo.task = curctx->task;
+
+		/* build the stack frame on the softirq stack */
+		isp = (u32 *) ((char *)irqctx + sizeof(struct thread_info));
+
+		asm volatile(
+			"MOV   D0.5,%0\n"
+			"SWAP  A0StP,D0.5\n"
+			"CALLR D1RtP,___do_softirq\n"
+			"MOV   A0StP,D0.5\n"
+			:
+			: "r" (isp)
+			: "memory", "cc", "D1Ar1", "D0Ar2", "D1Ar3", "D0Ar4",
+			  "D1Ar5", "D0Ar6", "D0Re0", "D1Re0", "D0.4", "D1RtP",
+			  "D0.5"
+			);
+		/*
+		 * Shouldn't happen, we returned above if in_interrupt():
+		 */
+		WARN_ON_ONCE(softirq_count());
+	}
+
+	local_irq_restore(flags);
+}
+#endif
+
+static struct irq_chip meta_irq_type = {
+	.name = "META-IRQ",
+	.irq_startup = startup_meta_irq,
+	.irq_shutdown = shutdown_meta_irq,
+};
+
+/*
+ * void init_IRQ(void)
+ *
+ * Parameters:	None
+ *
+ * Returns:	Nothing
+ *
+ * This function should be called during kernel startup to initialize
+ * the IRQ handling routines.
+ */
+void __init init_IRQ(void)
+{
+	int i;
+
+	for (i = 0; i < META_IRQS; i++)
+#ifdef CONFIG_SMP
+		irq_set_chip_and_handler(i, &meta_irq_type, handle_percpu_irq);
+#else
+		irq_set_chip_and_handler(i, &meta_irq_type, handle_simple_irq);
+#endif
+
+	irq_ctx_init(smp_processor_id());
+
+	init_internal_IRQ();
+	init_external_IRQ();
+
+	if (machine_desc->init_irq)
+		machine_desc->init_irq();
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static void route_irq(struct irq_data *data, unsigned int irq, unsigned int cpu)
+{
+	struct irq_desc *desc = irq_to_desc(irq);
+	struct irq_chip *chip = irq_data_get_irq_chip(data);
+
+	raw_spin_lock_irq(&desc->lock);
+	if (chip->irq_set_affinity)
+		chip->irq_set_affinity(data, cpumask_of(cpu), false);
+	raw_spin_unlock_irq(&desc->lock);
+}
+
+/*
+ * The CPU has been marked offline.  Migrate IRQs off this CPU.  If
+ * the affinity settings do not allow other CPUs, force them onto any
+ * available CPU.
+ */
+void migrate_irqs(void)
+{
+	unsigned int i, cpu = smp_processor_id();
+
+	for (i = 0; i < NR_IRQS; i++) {
+		struct irq_data *data = irq_get_irq_data(i);
+		unsigned int newcpu;
+
+		if (irqd_is_per_cpu(data))
+			continue;
+
+		if (!cpumask_test_cpu(cpu, data->affinity))
+			continue;
+
+		newcpu = cpumask_any_and(data->affinity, cpu_online_mask);
+
+		if (newcpu >= nr_cpu_ids) {
+			pr_info_ratelimited("IRQ%u no longer affine to CPU%u\n",
+					    i, cpu);
+
+			cpumask_setall(data->affinity);
+			newcpu = cpumask_any_and(data->affinity,
+						 cpu_online_mask);
+		}
+
+		route_irq(data, i, newcpu);
+	}
+}
+#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/metag/kernel/irq_external.c b/arch/metag/kernel/irq_external.c
new file mode 100644
index 0000000..a25edaf
--- /dev/null
+++ b/arch/metag/kernel/irq_external.c
@@ -0,0 +1,886 @@
+/*
+ * Meta External interrupt code.
+ *
+ * Copyright (C) 2005-2012 Imagination Technologies Ltd.
+ *
+ * External interrupts on Meta are configured at two-levels, in the CPU core and
+ * in the external trigger block. Interrupts from SoC peripherals are
+ * multiplexed onto a single Meta CPU "trigger" - traditionally it has always
+ * been trigger 2 (TR2). For info on how de-multiplexing happens check out
+ * meta_intc_irq_demux().
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+
+#include <asm/irq.h>
+#include <asm/irq_external.h>
+#include <asm/hwthread.h>
+
+#define HWSTAT_STRIDE 8
+#define HWVEC_BLK_STRIDE 0x1000
+
+/**
+ * struct meta_intc_priv - private meta external interrupt data
+ * @nr_banks:		Number of interrupt banks
+ * @domain:		IRQ domain for all banks of external IRQs
+ * @unmasked:		Record of unmasked IRQs
+ * @levels_altered:	Record of altered level bits
+ */
+struct meta_intc_priv {
+	unsigned int		nr_banks;
+	struct irq_domain	*domain;
+
+	unsigned long		unmasked[4];
+
+#ifdef CONFIG_METAG_SUSPEND_MEM
+	unsigned long		levels_altered[4];
+#endif
+};
+
+/* Private data for the one and only external interrupt controller */
+static struct meta_intc_priv meta_intc_priv;
+
+/**
+ * meta_intc_offset() - Get the offset into the bank of a hardware IRQ number
+ * @hw:		Hardware IRQ number (within external trigger block)
+ *
+ * Returns:	Bit offset into the IRQ's bank registers
+ */
+static unsigned int meta_intc_offset(irq_hw_number_t hw)
+{
+	return hw & 0x1f;
+}
+
+/**
+ * meta_intc_bank() - Get the bank number of a hardware IRQ number
+ * @hw:		Hardware IRQ number (within external trigger block)
+ *
+ * Returns:	Bank number indicating which register the IRQ's bits are
+ */
+static unsigned int meta_intc_bank(irq_hw_number_t hw)
+{
+	return hw >> 5;
+}
+
+/**
+ * meta_intc_stat_addr() - Get the address of a HWSTATEXT register
+ * @hw:		Hardware IRQ number (within external trigger block)
+ *
+ * Returns:	Address of a HWSTATEXT register containing the status bit for
+ *		the specified hardware IRQ number
+ */
+static void __iomem *meta_intc_stat_addr(irq_hw_number_t hw)
+{
+	return (void __iomem *)(HWSTATEXT +
+				HWSTAT_STRIDE * meta_intc_bank(hw));
+}
+
+/**
+ * meta_intc_level_addr() - Get the address of a HWLEVELEXT register
+ * @hw:		Hardware IRQ number (within external trigger block)
+ *
+ * Returns:	Address of a HWLEVELEXT register containing the sense bit for
+ *		the specified hardware IRQ number
+ */
+static void __iomem *meta_intc_level_addr(irq_hw_number_t hw)
+{
+	return (void __iomem *)(HWLEVELEXT +
+				HWSTAT_STRIDE * meta_intc_bank(hw));
+}
+
+/**
+ * meta_intc_mask_addr() - Get the address of a HWMASKEXT register
+ * @hw:		Hardware IRQ number (within external trigger block)
+ *
+ * Returns:	Address of a HWMASKEXT register containing the mask bit for the
+ *		specified hardware IRQ number
+ */
+static void __iomem *meta_intc_mask_addr(irq_hw_number_t hw)
+{
+	return (void __iomem *)(HWMASKEXT +
+				HWSTAT_STRIDE * meta_intc_bank(hw));
+}
+
+/**
+ * meta_intc_vec_addr() - Get the vector address of a hardware interrupt
+ * @hw:		Hardware IRQ number (within external trigger block)
+ *
+ * Returns:	Address of a HWVECEXT register controlling the core trigger to
+ *		vector the IRQ onto
+ */
+static inline void __iomem *meta_intc_vec_addr(irq_hw_number_t hw)
+{
+	return (void __iomem *)(HWVEC0EXT +
+				HWVEC_BLK_STRIDE * meta_intc_bank(hw) +
+				HWVECnEXT_STRIDE * meta_intc_offset(hw));
+}
+
+/**
+ * meta_intc_startup_irq() - set up an external irq
+ * @data:	data for the external irq to start up
+ *
+ * Multiplex interrupts for irq onto TR2. Clear any pending interrupts and
+ * unmask irq, both using the appropriate callbacks.
+ */
+static unsigned int meta_intc_startup_irq(struct irq_data *data)
+{
+	irq_hw_number_t hw = data->hwirq;
+	void __iomem *vec_addr = meta_intc_vec_addr(hw);
+	int thread = hard_processor_id();
+
+	/* Perform any necessary acking. */
+	if (data->chip->irq_ack)
+		data->chip->irq_ack(data);
+
+	/* Wire up this interrupt to the core with HWVECxEXT. */
+	writel(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr);
+
+	/* Perform any necessary unmasking. */
+	data->chip->irq_unmask(data);
+
+	return 0;
+}
+
+/**
+ * meta_intc_shutdown_irq() - turn off an external irq
+ * @data:	data for the external irq to turn off
+ *
+ * Mask irq using the appropriate callback and stop muxing it onto TR2.
+ */
+static void meta_intc_shutdown_irq(struct irq_data *data)
+{
+	irq_hw_number_t hw = data->hwirq;
+	void __iomem *vec_addr = meta_intc_vec_addr(hw);
+
+	/* Mask the IRQ */
+	data->chip->irq_mask(data);
+
+	/*
+	 * Disable the IRQ at the core by removing the interrupt from
+	 * the HW vector mapping.
+	 */
+	writel(0, vec_addr);
+}
+
+/**
+ * meta_intc_ack_irq() - acknowledge an external irq
+ * @data:	data for the external irq to ack
+ *
+ * Clear down an edge interrupt in the status register.
+ */
+static void meta_intc_ack_irq(struct irq_data *data)
+{
+	irq_hw_number_t hw = data->hwirq;
+	unsigned int bit = 1 << meta_intc_offset(hw);
+	void __iomem *stat_addr = meta_intc_stat_addr(hw);
+
+	/* Ack the int, if it is still 'on'.
+	 * NOTE - this only works for edge triggered interrupts.
+	 */
+	if (readl(stat_addr) & bit)
+		writel(bit, stat_addr);
+}
+
+/**
+ * record_irq_is_masked() - record the IRQ masked so it doesn't get handled
+ * @data:	data for the external irq to record
+ *
+ * This should get called whenever an external IRQ is masked (by whichever
+ * callback is used). It records the IRQ masked so that it doesn't get handled
+ * if it still shows up in the status register.
+ */
+static void record_irq_is_masked(struct irq_data *data)
+{
+	struct meta_intc_priv *priv = &meta_intc_priv;
+	irq_hw_number_t hw = data->hwirq;
+
+	clear_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]);
+}
+
+/**
+ * record_irq_is_unmasked() - record the IRQ unmasked so it can be handled
+ * @data:	data for the external irq to record
+ *
+ * This should get called whenever an external IRQ is unmasked (by whichever
+ * callback is used). It records the IRQ unmasked so that it gets handled if it
+ * shows up in the status register.
+ */
+static void record_irq_is_unmasked(struct irq_data *data)
+{
+	struct meta_intc_priv *priv = &meta_intc_priv;
+	irq_hw_number_t hw = data->hwirq;
+
+	set_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]);
+}
+
+/*
+ * For use by wrapper IRQ drivers
+ */
+
+/**
+ * meta_intc_mask_irq_simple() - minimal mask used by wrapper IRQ drivers
+ * @data:	data for the external irq being masked
+ *
+ * This should be called by any wrapper IRQ driver mask functions. it doesn't do
+ * any masking but records the IRQ as masked so that the core code knows the
+ * mask has taken place. It is the callers responsibility to ensure that the IRQ
+ * won't trigger an interrupt to the core.
+ */
+void meta_intc_mask_irq_simple(struct irq_data *data)
+{
+	record_irq_is_masked(data);
+}
+
+/**
+ * meta_intc_unmask_irq_simple() - minimal unmask used by wrapper IRQ drivers
+ * @data:	data for the external irq being unmasked
+ *
+ * This should be called by any wrapper IRQ driver unmask functions. it doesn't
+ * do any unmasking but records the IRQ as unmasked so that the core code knows
+ * the unmask has taken place. It is the callers responsibility to ensure that
+ * the IRQ can now trigger an interrupt to the core.
+ */
+void meta_intc_unmask_irq_simple(struct irq_data *data)
+{
+	record_irq_is_unmasked(data);
+}
+
+
+/**
+ * meta_intc_mask_irq() - mask an external irq using HWMASKEXT
+ * @data:	data for the external irq to mask
+ *
+ * This is a default implementation of a mask function which makes use of the
+ * HWMASKEXT registers available in newer versions.
+ *
+ * Earlier versions without these registers should use SoC level IRQ masking
+ * which call the meta_intc_*_simple() functions above, or if that isn't
+ * available should use the fallback meta_intc_*_nomask() functions below.
+ */
+static void meta_intc_mask_irq(struct irq_data *data)
+{
+	irq_hw_number_t hw = data->hwirq;
+	unsigned int bit = 1 << meta_intc_offset(hw);
+	void __iomem *mask_addr = meta_intc_mask_addr(hw);
+	unsigned long flags;
+
+	record_irq_is_masked(data);
+
+	/* update the interrupt mask */
+	__global_lock2(flags);
+	writel(readl(mask_addr) & ~bit, mask_addr);
+	__global_unlock2(flags);
+}
+
+/**
+ * meta_intc_unmask_irq() - unmask an external irq using HWMASKEXT
+ * @data:	data for the external irq to unmask
+ *
+ * This is a default implementation of an unmask function which makes use of the
+ * HWMASKEXT registers available on new versions. It should be paired with
+ * meta_intc_mask_irq() above.
+ */
+static void meta_intc_unmask_irq(struct irq_data *data)
+{
+	irq_hw_number_t hw = data->hwirq;
+	unsigned int bit = 1 << meta_intc_offset(hw);
+	void __iomem *mask_addr = meta_intc_mask_addr(hw);
+	unsigned long flags;
+
+	record_irq_is_unmasked(data);
+
+	/* update the interrupt mask */
+	__global_lock2(flags);
+	writel(readl(mask_addr) | bit, mask_addr);
+	__global_unlock2(flags);
+}
+
+/**
+ * meta_intc_mask_irq_nomask() - mask an external irq by unvectoring
+ * @data:	data for the external irq to mask
+ *
+ * This is the version of the mask function for older versions which don't have
+ * HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the IRQ is
+ * unvectored from the core and retriggered if necessary later.
+ */
+static void meta_intc_mask_irq_nomask(struct irq_data *data)
+{
+	irq_hw_number_t hw = data->hwirq;
+	void __iomem *vec_addr = meta_intc_vec_addr(hw);
+
+	record_irq_is_masked(data);
+
+	/* there is no interrupt mask, so unvector the interrupt */
+	writel(0, vec_addr);
+}
+
+/**
+ * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring
+ * @data:	data for the external irq to unmask
+ *
+ * This is the version of the unmask function for older versions which don't
+ * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the
+ * IRQ is revectored back to the core and retriggered if necessary.
+ *
+ * The retriggering done by this function is specific to edge interrupts.
+ */
+static void meta_intc_unmask_edge_irq_nomask(struct irq_data *data)
+{
+	irq_hw_number_t hw = data->hwirq;
+	unsigned int bit = 1 << meta_intc_offset(hw);
+	void __iomem *stat_addr = meta_intc_stat_addr(hw);
+	void __iomem *vec_addr = meta_intc_vec_addr(hw);
+	unsigned int thread = hard_processor_id();
+
+	record_irq_is_unmasked(data);
+
+	/* there is no interrupt mask, so revector the interrupt */
+	writel(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr);
+
+	/*
+	 * Re-trigger interrupt
+	 *
+	 * Writing a 1 toggles, and a 0->1 transition triggers. We only
+	 * retrigger if the status bit is already set, which means we
+	 * need to clear it first. Retriggering is fundamentally racy
+	 * because if the interrupt fires again after we clear it we
+	 * could end up clearing it again and the interrupt handler
+	 * thinking it hasn't fired. Therefore we need to keep trying to
+	 * retrigger until the bit is set.
+	 */
+	if (readl(stat_addr) & bit) {
+		writel(bit, stat_addr);
+		while (!(readl(stat_addr) & bit))
+			writel(bit, stat_addr);
+	}
+}
+
+/**
+ * meta_intc_unmask_level_irq_nomask() - unmask a level irq by revectoring
+ * @data:	data for the external irq to unmask
+ *
+ * This is the version of the unmask function for older versions which don't
+ * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the
+ * IRQ is revectored back to the core and retriggered if necessary.
+ *
+ * The retriggering done by this function is specific to level interrupts.
+ */
+static void meta_intc_unmask_level_irq_nomask(struct irq_data *data)
+{
+	irq_hw_number_t hw = data->hwirq;
+	unsigned int bit = 1 << meta_intc_offset(hw);
+	void __iomem *stat_addr = meta_intc_stat_addr(hw);
+	void __iomem *vec_addr = meta_intc_vec_addr(hw);
+	unsigned int thread = hard_processor_id();
+
+	record_irq_is_unmasked(data);
+
+	/* there is no interrupt mask, so revector the interrupt */
+	writel(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr);
+
+	/* Re-trigger interrupt */
+	/* Writing a 1 triggers interrupt */
+	if (readl(stat_addr) & bit)
+		writel(bit, stat_addr);
+}
+
+/**
+ * meta_intc_irq_set_type() - set the type of an external irq
+ * @data:	data for the external irq to set the type of
+ * @flow_type:	new irq flow type
+ *
+ * Set the flow type of an external interrupt. This updates the irq chip and irq
+ * handler depending on whether the irq is edge or level sensitive (the polarity
+ * is ignored), and also sets up the bit in HWLEVELEXT so the hardware knows
+ * when to trigger.
+ */
+static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+#ifdef CONFIG_METAG_SUSPEND_MEM
+	struct meta_intc_priv *priv = &meta_intc_priv;
+#endif
+	unsigned int irq = data->irq;
+	irq_hw_number_t hw = data->hwirq;
+	unsigned int bit = 1 << meta_intc_offset(hw);
+	void __iomem *level_addr = meta_intc_level_addr(hw);
+	unsigned long flags;
+	unsigned int level;
+
+	/* update the chip/handler */
+	if (flow_type & IRQ_TYPE_LEVEL_MASK)
+		irq_set_chip_and_handler(irq, &meta_intc_level_chip,
+					 handle_level_irq);
+	else
+		irq_set_chip_and_handler(irq, &meta_intc_edge_chip,
+					 handle_edge_irq);
+
+	/* and clear/set the bit in HWLEVELEXT */
+	__global_lock2(flags);
+	level = readl(level_addr);
+	if (flow_type & IRQ_TYPE_LEVEL_MASK)
+		level |= bit;
+	else
+		level &= ~bit;
+	writel(level, level_addr);
+#ifdef CONFIG_METAG_SUSPEND_MEM
+	priv->levels_altered[meta_intc_bank(hw)] |= bit;
+#endif
+	__global_unlock2(flags);
+
+	return 0;
+}
+
+/**
+ * meta_intc_irq_demux() - external irq de-multiplexer
+ * @irq:	the virtual interrupt number
+ * @desc:	the interrupt description structure for this irq
+ *
+ * The cpu receives an interrupt on TR2 when a SoC interrupt has occurred. It is
+ * this function's job to demux this irq and figure out exactly which external
+ * irq needs servicing.
+ *
+ * Whilst using TR2 to detect external interrupts is a software convention it is
+ * (hopefully) unlikely to change.
+ */
+static void meta_intc_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+	struct meta_intc_priv *priv = &meta_intc_priv;
+	unsigned int bank, irq_no, status;
+	void __iomem *stat_addr = meta_intc_stat_addr(0);
+
+	/*
+	 * Locate which interrupt has caused our handler to run.
+	 */
+	for (bank = 0; bank < priv->nr_banks; ++bank) {
+		/* Which interrupts are currently pending in this bank? */
+recalculate:
+		status = readl(stat_addr) & priv->unmasked[bank];
+
+		irq_no = HWSTATEXT_TO_IRQ(bank * 32);
+		for (; status; status >>= 1, ++irq_no) {
+			if (status & 0x1) {
+				/*
+				 * Only fire off external interrupts that are
+				 * registered to be handled by the kernel.
+				 * Other external interrupts are probably being
+				 * handled by other Meta hardware threads.
+				 */
+				generic_handle_irq(irq_no);
+
+				/*
+				 * The handler may have re-enabled interrupts
+				 * which could have caused a nested invocation
+				 * of this code and make the copy of the
+				 * status register we are using invalid.
+				 */
+				goto recalculate;
+			}
+		}
+		stat_addr += HWSTAT_STRIDE;
+	}
+}
+
+#ifdef CONFIG_SMP
+/**
+ * meta_intc_set_affinity() - set the affinity for an interrupt
+ * @data:	data for the external irq to set the affinity of
+ * @cpumask:	cpu mask representing cpus which can handle the interrupt
+ * @force:	whether to force (ignored)
+ *
+ * Revector the specified external irq onto a specific cpu's TR2 trigger, so
+ * that that cpu tends to be the one who handles it.
+ */
+static int meta_intc_set_affinity(struct irq_data *data,
+				  const struct cpumask *cpumask, bool force)
+{
+	irq_hw_number_t hw = data->hwirq;
+	void __iomem *vec_addr = meta_intc_vec_addr(hw);
+	unsigned int cpu, thread;
+
+	/*
+	 * Wire up this interrupt from HWVECxEXT to the Meta core.
+	 *
+	 * Note that we can't wire up HWVECxEXT to interrupt more than
+	 * one cpu (the interrupt code doesn't support it), so we just
+	 * pick the first cpu we find in 'cpumask'.
+	 */
+	cpu = cpumask_any(cpumask);
+	thread = cpu_2_hwthread_id[cpu];
+
+	writel(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr);
+
+	return 0;
+}
+#else
+#define meta_intc_set_affinity	NULL
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+#define META_INTC_CHIP_FLAGS	(IRQCHIP_MASK_ON_SUSPEND \
+				| IRQCHIP_SKIP_SET_WAKE)
+#else
+#define META_INTC_CHIP_FLAGS	0
+#endif
+
+/* public edge/level irq chips which SoCs can override */
+
+struct irq_chip meta_intc_edge_chip = {
+	.irq_startup		= meta_intc_startup_irq,
+	.irq_shutdown		= meta_intc_shutdown_irq,
+	.irq_ack		= meta_intc_ack_irq,
+	.irq_mask		= meta_intc_mask_irq,
+	.irq_unmask		= meta_intc_unmask_irq,
+	.irq_set_type		= meta_intc_irq_set_type,
+	.irq_set_affinity	= meta_intc_set_affinity,
+	.flags			= META_INTC_CHIP_FLAGS,
+};
+
+struct irq_chip meta_intc_level_chip = {
+	.irq_startup		= meta_intc_startup_irq,
+	.irq_shutdown		= meta_intc_shutdown_irq,
+	.irq_set_type		= meta_intc_irq_set_type,
+	.irq_mask		= meta_intc_mask_irq,
+	.irq_unmask		= meta_intc_unmask_irq,
+	.irq_set_affinity	= meta_intc_set_affinity,
+	.flags			= META_INTC_CHIP_FLAGS,
+};
+
+/**
+ * meta_intc_map() - map an external irq
+ * @d:		irq domain of external trigger block
+ * @irq:	virtual irq number
+ * @hw:		hardware irq number within external trigger block
+ *
+ * This sets up a virtual irq for a specified hardware interrupt. The irq chip
+ * and handler is configured, using the HWLEVELEXT registers to determine
+ * edge/level flow type. These registers will have been set when the irq type is
+ * set (or set to a default at init time).
+ */
+static int meta_intc_map(struct irq_domain *d, unsigned int irq,
+			 irq_hw_number_t hw)
+{
+	unsigned int bit = 1 << meta_intc_offset(hw);
+	void __iomem *level_addr = meta_intc_level_addr(hw);
+
+	/* Go by the current sense in the HWLEVELEXT register */
+	if (readl(level_addr) & bit)
+		irq_set_chip_and_handler(irq, &meta_intc_level_chip,
+					 handle_level_irq);
+	else
+		irq_set_chip_and_handler(irq, &meta_intc_edge_chip,
+					 handle_edge_irq);
+	return 0;
+}
+
+static const struct irq_domain_ops meta_intc_domain_ops = {
+	.map = meta_intc_map,
+	.xlate = irq_domain_xlate_twocell,
+};
+
+#ifdef CONFIG_METAG_SUSPEND_MEM
+
+/**
+ * struct meta_intc_context - suspend context
+ * @levels:	State of HWLEVELEXT registers
+ * @masks:	State of HWMASKEXT registers
+ * @vectors:	State of HWVECEXT registers
+ * @txvecint:	State of TxVECINT registers
+ *
+ * This structure stores the IRQ state across suspend.
+ */
+struct meta_intc_context {
+	u32 levels[4];
+	u32 masks[4];
+	u8 vectors[4*32];
+
+	u8 txvecint[4][4];
+};
+
+/* suspend context */
+static struct meta_intc_context *meta_intc_context;
+
+/**
+ * meta_intc_suspend() - store irq state
+ *
+ * To avoid interfering with other threads we only save the IRQ state of IRQs in
+ * use by Linux.
+ */
+static int meta_intc_suspend(void)
+{
+	struct meta_intc_priv *priv = &meta_intc_priv;
+	int i, j;
+	irq_hw_number_t hw;
+	unsigned int bank;
+	unsigned long flags;
+	struct meta_intc_context *context;
+	void __iomem *level_addr, *mask_addr, *vec_addr;
+	u32 mask, bit;
+
+	context = kzalloc(sizeof(*context), GFP_ATOMIC);
+	if (!context)
+		return -ENOMEM;
+
+	hw = 0;
+	i = HWSTATEXT_TO_IRQ(0);
+	level_addr = meta_intc_level_addr(0);
+	mask_addr = meta_intc_mask_addr(0);
+	for (bank = 0; bank < priv->nr_banks; ++bank) {
+		vec_addr = meta_intc_vec_addr(hw);
+
+		/* create mask of interrupts in use */
+		mask = 0;
+		for (bit = 1; bit; bit <<= 1) {
+			/* save only enabled interrupts or those with actions */
+			if (!irqd_irq_disabled(irq_get_irq_data(i)) ||
+			    irq_has_action(i)) {
+				mask |= bit;
+
+				/* save trigger vector */
+				context->vectors[hw] = readl(vec_addr);
+			}
+
+			++i;
+			++hw;
+			vec_addr += HWVECnEXT_STRIDE;
+		}
+
+		/* save level state if any IRQ levels altered */
+		if (priv->levels_altered[bank])
+			context->levels[bank] = readl(level_addr);
+		/* save mask state if any IRQs in use */
+		if (mask)
+			context->masks[bank] = readl(mask_addr);
+
+		level_addr += HWSTAT_STRIDE;
+		mask_addr += HWSTAT_STRIDE;
+	}
+
+	/* save trigger matrixing */
+	__global_lock2(flags);
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			context->txvecint[i][j] = readl(T0VECINT_BHALT +
+							TnVECINT_STRIDE*i +
+							8*j);
+	__global_unlock2(flags);
+
+	meta_intc_context = context;
+	return 0;
+}
+
+/**
+ * meta_intc_resume() - restore saved irq state
+ *
+ * Restore the saved IRQ state and drop it.
+ */
+static void meta_intc_resume(void)
+{
+	struct meta_intc_priv *priv = &meta_intc_priv;
+	int i, j;
+	irq_hw_number_t hw;
+	unsigned int bank;
+	unsigned long flags;
+	struct meta_intc_context *context = meta_intc_context;
+	void __iomem *level_addr, *mask_addr, *vec_addr;
+	u32 mask, bit, tmp;
+
+	meta_intc_context = NULL;
+
+	hw = 0;
+	i = HWSTATEXT_TO_IRQ(0);
+	level_addr = meta_intc_level_addr(0);
+	mask_addr = meta_intc_mask_addr(0);
+	for (bank = 0; bank < priv->nr_banks; ++bank) {
+		vec_addr = meta_intc_vec_addr(hw);
+
+		/* create mask of interrupts in use */
+		mask = 0;
+		for (bit = 1; bit; bit <<= 1) {
+			/* restore enabled interrupts or those with actions */
+			if (!irqd_irq_disabled(irq_get_irq_data(i)) ||
+			    irq_has_action(i)) {
+				mask |= bit;
+
+				/* restore trigger vector */
+				writel(context->vectors[hw], vec_addr);
+			}
+
+			++i;
+			++hw;
+			vec_addr += HWVECnEXT_STRIDE;
+		}
+
+		if (mask) {
+			/* restore mask state */
+			__global_lock2(flags);
+			tmp = readl(mask_addr);
+			tmp = (tmp & ~mask) | (context->masks[bank] & mask);
+			writel(tmp, mask_addr);
+			__global_unlock2(flags);
+		}
+
+		mask = priv->levels_altered[bank];
+		if (mask) {
+			/* restore level state */
+			__global_lock2(flags);
+			tmp = readl(level_addr);
+			tmp = (tmp & ~mask) | (context->levels[bank] & mask);
+			writel(tmp, level_addr);
+			__global_unlock2(flags);
+		}
+
+		level_addr += HWSTAT_STRIDE;
+		mask_addr += HWSTAT_STRIDE;
+	}
+
+	/* restore trigger matrixing */
+	__global_lock2(flags);
+	for (i = 0; i < 4; ++i) {
+		for (j = 0; j < 4; ++j) {
+			writel(context->txvecint[i][j],
+			       T0VECINT_BHALT +
+			       TnVECINT_STRIDE*i +
+			       8*j);
+		}
+	}
+	__global_unlock2(flags);
+
+	kfree(context);
+}
+
+static struct syscore_ops meta_intc_syscore_ops = {
+	.suspend = meta_intc_suspend,
+	.resume = meta_intc_resume,
+};
+
+static void __init meta_intc_init_syscore_ops(struct meta_intc_priv *priv)
+{
+	register_syscore_ops(&meta_intc_syscore_ops);
+}
+#else
+#define meta_intc_init_syscore_ops(priv) do {} while (0)
+#endif
+
+/**
+ * meta_intc_init_cpu() - register with a Meta cpu
+ * @priv:	private interrupt controller data
+ * @cpu:	the CPU to register on
+ *
+ * Configure @cpu's TR2 irq so that we can demux external irqs.
+ */
+static void __init meta_intc_init_cpu(struct meta_intc_priv *priv, int cpu)
+{
+	unsigned int thread = cpu_2_hwthread_id[cpu];
+	unsigned int signum = TBID_SIGNUM_TR2(thread);
+
+	/* Register the multiplexed IRQ handler */
+	irq_set_chained_handler(signum, meta_intc_irq_demux);
+	irq_set_irq_type(signum, IRQ_TYPE_LEVEL_LOW);
+}
+
+/**
+ * meta_intc_no_mask() - indicate lack of HWMASKEXT registers
+ *
+ * Called from SoC code (or init code below) to dynamically indicate the lack of
+ * HWMASKEXT registers (for example depending on some SoC revision register).
+ * This alters the irq mask and unmask callbacks to use the fallback
+ * unvectoring/retriggering technique instead of using HWMASKEXT registers.
+ */
+void __init meta_intc_no_mask(void)
+{
+	meta_intc_edge_chip.irq_mask	= meta_intc_mask_irq_nomask;
+	meta_intc_edge_chip.irq_unmask	= meta_intc_unmask_edge_irq_nomask;
+	meta_intc_level_chip.irq_mask	= meta_intc_mask_irq_nomask;
+	meta_intc_level_chip.irq_unmask	= meta_intc_unmask_level_irq_nomask;
+}
+
+/**
+ * init_external_IRQ() - initialise the external irq controller
+ *
+ * Set up the external irq controller using device tree properties. This is
+ * called from init_IRQ().
+ */
+int __init init_external_IRQ(void)
+{
+	struct meta_intc_priv *priv = &meta_intc_priv;
+	struct device_node *node;
+	int ret, cpu;
+	u32 val;
+	u32 vals[4];
+	bool no_masks = false;
+	unsigned int i;
+	void __iomem *level_addr;
+
+	node = of_find_compatible_node(NULL, NULL, "img,meta-intc");
+	if (!node)
+		return -ENOENT;
+
+	/* Get number of banks */
+	ret = of_property_read_u32(node, "num-banks", &val);
+	if (ret) {
+		pr_err("meta-intc: No num-banks property found\n");
+		return ret;
+	}
+	if (val < 1 || val > 4) {
+		pr_err("meta-intc: num-banks (%u) out of range\n", val);
+		return -EINVAL;
+	}
+	priv->nr_banks = val;
+
+	/* Are any mask registers present? */
+	if (of_get_property(node, "no-mask", NULL))
+		no_masks = true;
+
+	/* Are any default edge/level senses available? */
+	ret = of_property_read_u32_array(node, "default-level", vals,
+					 priv->nr_banks);
+	if (!ret) {
+		/* valid, set HWLEVELEXT registers */
+		level_addr = (void __iomem *)HWLEVELEXT;
+		for (i = 0; i < priv->nr_banks; ++i,
+						level_addr += HWSTAT_STRIDE) {
+			writel(vals[i], level_addr);
+#ifdef CONFIG_METAG_SUSPEND_MEM
+			priv->levels_altered[i] = 0xffffffff;
+#endif
+		}
+	} else if (ret != -EINVAL) {
+		/* invalid (rather than simply omitted) */
+		pr_err("meta-intc: default-level could not be read\n");
+		return ret;
+	}
+
+	/* No HWMASKEXT registers present? */
+	if (no_masks)
+		meta_intc_no_mask();
+
+	/* Set up an IRQ domain */
+	/*
+	 * This is a legacy IRQ domain for now until all the platform setup code
+	 * has been converted to devicetree.
+	 */
+	priv->domain = irq_domain_add_legacy(node, priv->nr_banks*32,
+					     HWSTATEXT_TO_IRQ(0), 0,
+					     &meta_intc_domain_ops, priv);
+	if (unlikely(!priv->domain)) {
+		pr_err("meta-intc: cannot add IRQ domain\n");
+		return -ENOMEM;
+	}
+
+	/* Setup TR2 for all cpus. */
+	for_each_possible_cpu(cpu)
+		meta_intc_init_cpu(priv, cpu);
+
+	/* Set up system suspend/resume callbacks */
+	meta_intc_init_syscore_ops(priv);
+
+	pr_info("meta-intc: External IRQ controller initialised (%u IRQs)\n",
+		priv->nr_banks*32);
+
+	return 0;
+}
diff --git a/arch/metag/kernel/irq_internal.c b/arch/metag/kernel/irq_internal.c
new file mode 100644
index 0000000..6a4d56a
--- /dev/null
+++ b/arch/metag/kernel/irq_internal.c
@@ -0,0 +1,235 @@
+/*
+ * Meta internal (HWSTATMETA) interrupt code.
+ *
+ * Copyright (C) 2011 Imagination Technologies Ltd.
+ *
+ * This code is based on the code in SoC/common/irq.c and SoC/comet/irq.c
+ * The code base could be generalised/merged as a lot of the functionality is
+ * similar. Until this is done, we try to keep the code simple here.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <asm/hwthread.h>
+
+#define PERF0VECINT		0x04820580
+#define PERF1VECINT		0x04820588
+#define PERF0TRIG_OFFSET	16
+#define PERF1TRIG_OFFSET	17
+
+static unsigned int mapped; /* bit mask of mapped triggers */
+
+static unsigned int metag_internal_irq_startup_edge(struct irq_data *data);
+static void metag_internal_irq_shutdown(struct irq_data *data);
+static void metag_internal_irq_ack(struct irq_data *data);
+#ifdef CONFIG_SMP
+static int metag_internal_irq_set_affinity(struct irq_data *data,
+			const struct cpumask *cpumask, bool force);
+#endif
+
+static struct irq_chip internal_irq_edge_chip = {
+	.name = "HWSTATMETA-IRQ",
+	.irq_startup = metag_internal_irq_startup_edge,
+	.irq_shutdown = metag_internal_irq_shutdown,
+	.irq_ack = metag_internal_irq_ack,
+#ifdef CONFIG_SMP
+	.irq_set_affinity = metag_internal_irq_set_affinity,
+#endif
+};
+
+/*
+ *	metag_hwvec_addr - get the address of *VECINT regs of irq
+ *
+ *	This function is a table of supported triggers on HWSTATMETA
+ *	Could do with a structure, but better keep it simple. Changes
+ *	in this code should be rare.
+ */
+static inline void __iomem *metag_hwvec_addr(unsigned int irq)
+{
+	void __iomem *addr;
+	unsigned int offset = IRQ_TO_OFFSET(irq);
+
+	switch (offset) {
+	case PERF0TRIG_OFFSET:
+		addr = (void __iomem *)PERF0VECINT;
+		break;
+	case PERF1TRIG_OFFSET:
+		addr = (void __iomem *)PERF1VECINT;
+		break;
+	default:
+		addr = NULL;
+		break;
+	}
+	return addr;
+}
+
+/*
+ *	metag_internal_startup_edge_irq - setup an edge-triggered irq
+ *	@irq:	the irq to startup
+ *
+ *	Multiplex interrupts for @irq onto TR1. Clear any pending
+ *	interrupts.
+ */
+static unsigned int metag_internal_irq_startup_edge(struct irq_data *data)
+{
+	unsigned int irq = data->irq;
+	int thread = hard_processor_id();
+
+	/* Enable the interrupt by vectoring it */
+	writel(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), metag_hwvec_addr(irq));
+
+	/* Clear (toggle) the bit in HWSTATx for our interrupt. */
+	metag_internal_irq_ack(data);
+
+	return 0;
+}
+
+/*
+ *	metag_internal_irq_shutdown - turn off the irq
+ *	@irq:	the irq number to turn off
+ *
+ *	Mask @irq and clear any pending interrupts.
+ *	Stop muxing @irq onto TR1.
+ */
+static void metag_internal_irq_shutdown(struct irq_data *data)
+{
+	unsigned int irq = data->irq;
+	/*
+	 * Disable the IRQ at the core by removing the interrupt from
+	 * the HW vector mapping.
+	 */
+	writel(0, metag_hwvec_addr(irq));
+
+	/* Clear (toggle) the bit in HWSTATx for our interrupt. */
+	metag_internal_irq_ack(data);
+}
+
+/*
+ *	metag_internal_irq_ack - acknowledge irq
+ *	@irq:	the irq to ack
+ */
+static void metag_internal_irq_ack(struct irq_data *data)
+{
+	unsigned int irq = data->irq;
+	unsigned int offset = IRQ_TO_OFFSET(irq);
+	if (readl(HWSTATMETA) & (1 << offset))
+		writel(1 << offset, HWSTATMETA);
+}
+
+/*
+ * metag_internal_irq_status - returns the status of the mapped triggers
+ *
+ */
+
+static inline u32 metag_internal_irq_status(void)
+{
+	return readl(HWSTATMETA) & mapped;
+}
+
+#ifdef CONFIG_SMP
+/*
+ *	metag_internal_irq_set_affinity - set the affinity for an interrupt
+ */
+static int metag_internal_irq_set_affinity(struct irq_data *data,
+			const struct cpumask *cpumask, bool force)
+{
+	unsigned int cpu, thread;
+	unsigned int irq = data->irq;
+	/*
+	 * Wire up this interrupt from *VECINT to the Meta core.
+	 *
+	 * Note that we can't wire up *VECINT to interrupt more than
+	 * one cpu (the interrupt code doesn't support it), so we just
+	 * pick the first cpu we find in 'cpumask'.
+	 */
+	cpu = cpumask_any(cpumask);
+	thread = cpu_2_hwthread_id[cpu];
+
+	writel(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), metag_hwvec_addr(irq));
+
+	return 0;
+}
+#endif
+
+/*
+ *	metag_internal_irq_demux - irq de-multiplexer
+ *	@irq:	the interrupt number
+ *	@desc:	the interrupt description structure for this irq (unused)
+ *
+ *	The cpu receives an interrupt on TR1 when an interrupt has
+ *	occurred. It is this function's job to demux this irq and
+ *	figure out exactly which trigger needs servicing.
+ */
+static void metag_internal_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+	unsigned int irq_no;
+	u32 status;
+
+recalculate:
+	status = metag_internal_irq_status();
+
+	irq_no = HWSTATMETA_TO_IRQ(0);
+	while (status != 0) {
+		if (status & 0x1) {
+			/*
+			 * Only fire off interrupts that are
+			 * registered to be handled by the kernel.
+			 * Other interrupts are probably being
+			 * handled by other Meta hardware threads.
+			 */
+			generic_handle_irq(irq_no);
+
+			/*
+			 * The handler may have re-enabled interrupts
+			 * which could have caused a nested invocation
+			 * of this code and make the copy of the
+			 * status register we are using invalid.
+			 */
+			goto recalculate;
+		}
+		status >>= 1;
+		irq_no++;
+	}
+}
+
+/**
+ *	metag_internal_irq_init_cpu - regsister with the Meta cpu
+ *	@cpu:	the CPU to register on
+ *
+ *	Configure @cpu's TR1 irq so that we can demux irqs.
+ */
+static void metag_internal_irq_init_cpu(int cpu)
+{
+	unsigned int thread = cpu_2_hwthread_id[cpu];
+
+	/* Register the multiplexed IRQ handler */
+	irq_set_chained_handler(TBID_SIGNUM_TR1(thread),
+				metag_internal_irq_demux);
+	irq_set_irq_type(TBID_SIGNUM_TR1(thread), IRQ_TYPE_LEVEL_LOW);
+}
+
+/**
+ *	metag_internal_irq_register - register internal IRQs
+ *
+ *	Register the irq chip and handler function for all internal IRQs
+ */
+void __init init_internal_IRQ(void)
+{
+	unsigned int cpu;
+	unsigned int i;
+
+	for (i = HWSTATMETA_TO_IRQ(0); i < HWSTATEXT_TO_IRQ(0); i++) {
+		/* only register interrupt if it is mapped */
+		if (metag_hwvec_addr(i)) {
+			mapped |= (1 << IRQ_TO_OFFSET(i));
+			irq_set_chip_and_handler(i, &internal_irq_edge_chip,
+							handle_edge_irq);
+		}
+	}
+
+	/* Setup TR1 for all cpus. */
+	for_each_possible_cpu(cpu)
+		metag_internal_irq_init_cpu(cpu);
+};
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 18/40] metag: System Calls
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (14 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 17/40] metag: IRQ handling James Hogan
@ 2012-10-31 16:13 ` James Hogan
  2012-11-09 14:20   ` Arnd Bergmann
  2012-10-31 16:14 ` [RFC PATCH v1 19/40] metag: Scheduling/Process management James Hogan
                   ` (24 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:13 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add metag system call and gateway page interfaces. Metag uses the
generic system call numbers from asm-generic/unistd.h, as well as a
user gateway page mapped at 0x6ffff000 which contains fast atomic
primitives (depending on SMP) and a fast method of accessing TLS data.

System calls use the SWITCH instruction with the immediate 0x440001 to
signal a system call.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/mman.h         |   14 +++
 arch/metag/include/asm/syscall.h      |  104 ++++++++++++++++
 arch/metag/include/asm/syscalls.h     |   67 +++++++++++
 arch/metag/include/asm/unistd.h       |   28 +++++
 arch/metag/include/asm/user_gateway.h |   44 +++++++
 arch/metag/kernel/sys_metag.c         |  209 +++++++++++++++++++++++++++++++++
 arch/metag/kernel/user_gateway.S      |   99 ++++++++++++++++
 7 files changed, 565 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/mman.h
 create mode 100644 arch/metag/include/asm/syscall.h
 create mode 100644 arch/metag/include/asm/syscalls.h
 create mode 100644 arch/metag/include/asm/unistd.h
 create mode 100644 arch/metag/include/asm/user_gateway.h
 create mode 100644 arch/metag/kernel/sys_metag.c
 create mode 100644 arch/metag/kernel/user_gateway.S

diff --git a/arch/metag/include/asm/mman.h b/arch/metag/include/asm/mman.h
new file mode 100644
index 0000000..444a9c2
--- /dev/null
+++ b/arch/metag/include/asm/mman.h
@@ -0,0 +1,14 @@
+#ifndef __METAG_MMAN_H__
+#define __METAG_MMAN_H__
+
+#include <asm-generic/mman.h>
+
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+#define arch_mmap_check metag_mmap_check
+int metag_mmap_check(unsigned long addr, unsigned long len,
+		     unsigned long flags);
+#endif
+#endif
+
+#endif /* __METAG_MMAN_H__ */
diff --git a/arch/metag/include/asm/syscall.h b/arch/metag/include/asm/syscall.h
new file mode 100644
index 0000000..24fc979
--- /dev/null
+++ b/arch/metag/include/asm/syscall.h
@@ -0,0 +1,104 @@
+/*
+ * Access to user system call parameters and results
+ *
+ * Copyright (C) 2008 Imagination Technologies Ltd.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * See asm-generic/syscall.h for descriptions of what we must do here.
+ */
+
+#ifndef _ASM_METAG_SYSCALL_H
+#define _ASM_METAG_SYSCALL_H
+
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+
+#include <asm/switch.h>
+
+static inline long syscall_get_nr(struct task_struct *task,
+				  struct pt_regs *regs)
+{
+	unsigned long insn;
+
+	/*
+	 * FIXME there's no way to find out how we got here other than to
+	 * examine the memory at the PC to see if it is a syscall
+	 * SWITCH instruction.
+	 */
+	if (get_user(insn, (unsigned long *)(regs->ctx.CurrPC - 4)))
+		return -1;
+
+	if (insn == __METAG_SW_ENCODING(SYS))
+		return regs->ctx.DX[0].U1;
+	else
+		return -1L;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+				    struct pt_regs *regs)
+{
+	/* do nothing */
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+				     struct pt_regs *regs)
+{
+	unsigned long error = regs->ctx.DX[0].U0;
+	return IS_ERR_VALUE(error) ? error : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+					    struct pt_regs *regs)
+{
+	return regs->ctx.DX[0].U0;
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+					    struct pt_regs *regs,
+					    int error, long val)
+{
+	regs->ctx.DX[0].U0 = (long) error ?: val;
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 unsigned int i, unsigned int n,
+					 unsigned long *args)
+{
+	unsigned int reg, j;
+	BUG_ON(i + n > 6);
+
+	for (j = i, reg = 6 - i; j < (i + n); j++, reg--) {
+		if (reg % 2)
+			args[j] = regs->ctx.DX[(reg + 1) / 2].U0;
+		else
+			args[j] = regs->ctx.DX[reg / 2].U1;
+	}
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 unsigned int i, unsigned int n,
+					 const unsigned long *args)
+{
+	unsigned int reg;
+	BUG_ON(i + n > 6);
+
+	for (reg = 6 - i; i < (i + n); i++, reg--) {
+		if (reg % 2)
+			regs->ctx.DX[(reg + 1) / 2].U0 = args[i];
+		else
+			regs->ctx.DX[reg / 2].U1 = args[i];
+	}
+}
+
+#define NR_syscalls __NR_syscalls
+
+/* generic syscall table */
+extern const void *sys_call_table[];
+
+#endif	/* _ASM_METAG_SYSCALL_H */
diff --git a/arch/metag/include/asm/syscalls.h b/arch/metag/include/asm/syscalls.h
new file mode 100644
index 0000000..95405c3
--- /dev/null
+++ b/arch/metag/include/asm/syscalls.h
@@ -0,0 +1,67 @@
+#ifndef _ASM_METAG_SYSCALLS_H
+#define _ASM_METAG_SYSCALLS_H
+
+#include <linux/compiler.h>
+#include <linux/linkage.h>
+#include <linux/types.h>
+#include <linux/signal.h>
+
+/* kernel/process.c */
+asmlinkage int sys_fork(unsigned long, unsigned long,
+			unsigned long, unsigned long,
+			unsigned long, unsigned long,
+			struct pt_regs *);
+asmlinkage int sys_execve(char __user *, char __user *__user *,
+			  char __user *__user *, unsigned long,
+			  unsigned long, unsigned long,
+			  struct pt_regs *);
+asmlinkage int sys_clone(unsigned long, unsigned long,
+			 unsigned long, unsigned long,
+			 unsigned long, unsigned long,
+			 struct pt_regs *);
+
+/* kernel/signal.c */
+asmlinkage int sys_sigaltstack(const stack_t __user *, stack_t __user *,
+			       unsigned long, unsigned long,
+			       unsigned long, unsigned long,
+			       struct pt_regs *);
+asmlinkage int sys_rt_sigreturn(unsigned long, unsigned long,
+				unsigned long, unsigned long,
+				unsigned long, unsigned long,
+				struct pt_regs *);
+asmlinkage long sys_rt_sigaction(int sig,
+				 const struct sigaction __user *act,
+				 struct sigaction __user *oact,
+				 size_t sigsetsize);
+asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset,
+				  size_t sigsetsize);
+asmlinkage long sys_rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t __user *);
+
+/* kernel/sys_metag.c */
+asmlinkage long sys_mmap2(unsigned long, unsigned long, unsigned long,
+			  unsigned long, unsigned long, unsigned long);
+asmlinkage int sys_metag_spinlock(int __user *);
+asmlinkage int sys_metag_setglobalbit(char __user *, int);
+asmlinkage void sys_metag_set_fpu_flags(unsigned int);
+asmlinkage int sys_metag_set_tls(void __user *);
+asmlinkage void *sys_metag_get_tls(void);
+asmlinkage long sys32_truncate64(const char __user *, unsigned long,
+				 unsigned long);
+asmlinkage long sys32_ftruncate64(unsigned int, unsigned long,
+				  unsigned long);
+asmlinkage long sys32_fadvise64_64(int, unsigned long, unsigned long,
+				   unsigned long, unsigned long, int);
+asmlinkage long sys32_readahead(int, unsigned long, unsigned long, size_t);
+asmlinkage ssize_t sys32_pread64(unsigned long, char __user *, size_t,
+				 unsigned long, unsigned long);
+asmlinkage ssize_t sys32_pwrite64(unsigned long, char __user *, size_t,
+				  unsigned long, unsigned long);
+asmlinkage long sys32_sync_file_range(int, unsigned long, unsigned long,
+				      unsigned long, unsigned long,
+				      unsigned int);
+
+extern void do_notify_resume(struct pt_regs *regs, int from_syscall,
+			     unsigned int orig_syscall,
+			     unsigned long thread_info_flags);
+
+#endif /* _ASM_METAG_SYSCALLS_H */
diff --git a/arch/metag/include/asm/unistd.h b/arch/metag/include/asm/unistd.h
new file mode 100644
index 0000000..7322ba6
--- /dev/null
+++ b/arch/metag/include/asm/unistd.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+#if !defined(_ASM_METAG_UNISTD_H) || defined(__SYSCALL)
+#define _ASM_METAG_UNISTD_H
+
+/* Use the standard ABI for syscalls. */
+#include <asm-generic/unistd.h>
+
+/* metag-specific syscalls. */
+#define __NR_metag_spinlock		(__NR_arch_specific_syscall + 0)
+__SYSCALL(__NR_metag_spinlock, sys_metag_spinlock)
+#define __NR_metag_setglobalbit		(__NR_arch_specific_syscall + 1)
+__SYSCALL(__NR_metag_setglobalbit, sys_metag_setglobalbit)
+#define __NR_metag_set_fpu_flags	(__NR_arch_specific_syscall + 2)
+__SYSCALL(__NR_metag_set_fpu_flags, sys_metag_set_fpu_flags)
+#define __NR_metag_set_tls		(__NR_arch_specific_syscall + 3)
+__SYSCALL(__NR_metag_set_tls, sys_metag_set_tls)
+#define __NR_metag_get_tls		(__NR_arch_specific_syscall + 4)
+__SYSCALL(__NR_metag_get_tls, sys_metag_get_tls)
+
+#endif /* _ASM_METAG_UNISTD_H */
diff --git a/arch/metag/include/asm/user_gateway.h b/arch/metag/include/asm/user_gateway.h
new file mode 100644
index 0000000..aaf832b
--- /dev/null
+++ b/arch/metag/include/asm/user_gateway.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 Imagination Technologies
+ */
+
+#ifndef __ASM_METAG_USER_GATEWAY_H
+#define __ASM_METAG_USER_GATEWAY_H
+
+#include <asm/page.h>
+
+/* Page of kernel code accessible to userspace. */
+#define USER_GATEWAY_PAGE	0x6ffff000
+/* Offset of TLS pointer array in gateway page. */
+#define USER_GATEWAY_TLS	0x100
+
+#ifndef __ASSEMBLER__
+
+extern char __user_gateway_start;
+extern char __user_gateway_end;
+
+/* Kernel mapping of the gateway page. */
+extern void *gateway_page;
+
+static inline void set_gateway_tls(void __user *tls_ptr)
+{
+	void **gateway_tls = (void **)(gateway_page + USER_GATEWAY_TLS +
+				       hard_processor_id() * 4);
+
+	*gateway_tls = (__force void *)tls_ptr;
+#ifdef CONFIG_META12
+	/* Avoid cache aliases on virtually tagged cache. */
+	__builtin_dcache_flush((void *)USER_GATEWAY_PAGE + USER_GATEWAY_TLS +
+				       hard_processor_id() * sizeof(void *));
+#endif
+}
+
+extern int __kuser_get_tls(void);
+extern char *__kuser_get_tls_end[];
+
+extern int __kuser_cmpxchg(int, int, unsigned long *);
+extern char *__kuser_cmpxchg_end[];
+
+#endif
+
+#endif
diff --git a/arch/metag/kernel/sys_metag.c b/arch/metag/kernel/sys_metag.c
new file mode 100644
index 0000000..d7b3cee
--- /dev/null
+++ b/arch/metag/kernel/sys_metag.c
@@ -0,0 +1,209 @@
+/*
+ * This file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/Meta
+ * platform.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/syscalls.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/unistd.h>
+#include <asm/switch.h>
+#include <asm/syscall.h>
+#include <asm/syscalls.h>
+#include <asm/user_gateway.h>
+
+#define merge_64(hi, lo) ((((unsigned long long)(hi)) << 32) + \
+			  ((lo) & 0xffffffffUL))
+
+int metag_mmap_check(unsigned long addr, unsigned long len,
+		     unsigned long flags)
+{
+	/* We can't have people trying to write to the bottom of the
+	 * memory map, there are mysterious unspecified things there that
+	 * we don't want people trampling on.
+	 */
+	if ((flags & MAP_FIXED) && (addr < TASK_UNMAPPED_BASE))
+		return -EINVAL;
+
+	return 0;
+}
+
+asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
+			  unsigned long prot, unsigned long flags,
+			  unsigned long fd, unsigned long pgoff)
+{
+	/* The shift for mmap2 is constant, regardless of PAGE_SIZE setting. */
+	if (pgoff & ((1 << (PAGE_SHIFT - 12)) - 1))
+		return -EINVAL;
+
+	pgoff >>= PAGE_SHIFT - 12;
+
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
+}
+
+asmlinkage int sys_metag_spinlock(int __user *spinlock)
+{
+	int ret = 0, tmp;
+
+	local_irq_disable();
+	get_user(tmp, spinlock);
+	if (tmp)
+		ret = 1;
+	tmp = 1;
+	put_user(tmp, spinlock);
+	local_irq_enable();
+	return ret;
+}
+
+asmlinkage int sys_metag_setglobalbit(char __user *addr, int mask)
+{
+	char tmp;
+	int ret = 0;
+	unsigned int flags;
+
+	if (!((__force unsigned int)addr >= LINCORE_BASE))
+		return -EFAULT;
+
+	TBI_LOCK(flags);
+
+	__TBIDataCacheFlush((__force void *)addr, sizeof(mask));
+
+	ret = __get_user(tmp, addr);
+	if (ret)
+		goto out;
+	tmp |= mask;
+	ret = __put_user(tmp, addr);
+
+	__TBIDataCacheFlush((__force void *)addr, sizeof(mask));
+
+out:
+	TBI_UNLOCK(flags);
+
+	return ret;
+}
+
+/*
+ * Do a system call from kernel instead of calling sys_execve so we
+ * end up with proper pt_regs.
+ */
+int kernel_execve(const char *filename, const char *const argv[],
+		  const char *const envp[])
+{
+	register long __call __asm__("D1Re0") = __NR_execve;
+	register long __res __asm__("D0Re0");
+	register long __a __asm__("D1Ar1") = (long)(filename);
+	register long __b __asm__("D0Ar2") = (long)(argv);
+	register long __c __asm__("D1Ar3") = (long)(envp);
+	__asm__ __volatile__("SWITCH	#%c1"
+			     : "=d" (__res)
+			     : "i" (__METAG_SW_SYS), "d" (__call),
+			       "d" (__a), "d" (__b), "d" (__c)
+			     : "memory");
+	return __res;
+}
+
+#define TXDEFR_FPU_MASK ((0x1f << 16) | 0x1f)
+
+asmlinkage void sys_metag_set_fpu_flags(unsigned int flags)
+{
+	unsigned int temp;
+
+	flags &= TXDEFR_FPU_MASK;
+
+	__asm__ __volatile__("MOV %0,TXDEFR\n" : "=r" (temp));
+
+	temp &= ~TXDEFR_FPU_MASK;
+	temp |= flags;
+
+	__asm__ __volatile__("MOV TXDEFR,%0\n" : : "r" (temp));
+}
+
+asmlinkage int sys_metag_set_tls(void __user *ptr)
+{
+	current->thread.tls_ptr = ptr;
+	set_gateway_tls(ptr);
+
+	return 0;
+}
+
+asmlinkage void *sys_metag_get_tls(void)
+{
+	return (__force void *)current->thread.tls_ptr;
+}
+
+asmlinkage long sys32_truncate64(const char __user *path, unsigned long lo,
+				 unsigned long hi)
+{
+	return sys_truncate64(path, merge_64(hi, lo));
+}
+
+asmlinkage long sys32_ftruncate64(unsigned int fd, unsigned long lo,
+				  unsigned long hi)
+{
+	return sys_ftruncate64(fd, merge_64(hi, lo));
+}
+
+asmlinkage long sys32_fadvise64_64(int fd, unsigned long offs_lo,
+				   unsigned long offs_hi, unsigned long len_lo,
+				   unsigned long len_hi, int advice)
+{
+	return sys_fadvise64_64(fd, merge_64(offs_hi, offs_lo),
+				merge_64(len_hi, len_lo), advice);
+}
+
+asmlinkage long sys32_readahead(int fd, unsigned long lo, unsigned long hi,
+				size_t count)
+{
+	return sys_readahead(fd, merge_64(hi, lo), count);
+}
+
+asmlinkage ssize_t sys32_pread64(unsigned long fd, char __user *buf,
+				 size_t count, unsigned long lo,
+				 unsigned long hi)
+{
+	return sys_pread64(fd, buf, count, merge_64(hi, lo));
+}
+
+asmlinkage ssize_t sys32_pwrite64(unsigned long fd, char __user *buf,
+				  size_t count, unsigned long lo,
+				  unsigned long hi)
+{
+	return sys_pwrite64(fd, buf, count, merge_64(hi, lo));
+}
+
+asmlinkage long sys32_sync_file_range(int fd, unsigned long offs_lo,
+				      unsigned long offs_hi,
+				      unsigned long len_lo,
+				      unsigned long len_hi,
+				      unsigned int flags)
+{
+	return sys_sync_file_range(fd, merge_64(offs_hi, offs_lo),
+				   merge_64(len_hi, len_lo), flags);
+}
+
+/* Provide the actual syscall number to call mapping. */
+#undef __SYSCALL
+#define __SYSCALL(nr, call) [nr] = (call),
+
+/*
+ * Note that we can't include <linux/unistd.h> here since the header
+ * guard will defeat us; <asm/unistd.h> checks for __SYSCALL as well.
+ */
+const void *sys_call_table[__NR_syscalls] = {
+	[0 ... __NR_syscalls-1] = sys_ni_syscall,
+#include <asm/unistd.h>
+	/* we need wrappers for anything with unaligned 64bit arguments */
+	__SYSCALL(__NR_truncate64, sys32_truncate64)
+	__SYSCALL(__NR_ftruncate64, sys32_ftruncate64)
+	__SYSCALL(__NR_fadvise64_64, sys32_fadvise64_64)
+	__SYSCALL(__NR_readahead, sys32_readahead)
+	__SYSCALL(__NR_pread64, sys32_pread64)
+	__SYSCALL(__NR_pwrite64, sys32_pwrite64)
+	__SYSCALL(__NR_sync_file_range, sys32_sync_file_range)
+};
diff --git a/arch/metag/kernel/user_gateway.S b/arch/metag/kernel/user_gateway.S
new file mode 100644
index 0000000..c45e8b8
--- /dev/null
+++ b/arch/metag/kernel/user_gateway.S
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 Imagination Technologies Ltd.
+ *
+ * This file contains code that can be accessed from userspace and can
+ * access certain kernel data structures without the overhead of a system
+ * call.
+ */
+#define METAC_ALL_VALUES
+#define METAG_ALL_VALUES
+#include <asm/tbx/machine.inc>
+
+#include <asm/user_gateway.h>
+
+/*
+ * User helpers.
+ *
+ * These are segment of kernel provided user code reachable from user space
+ * at a fixed address in kernel memory.  This is used to provide user space
+ * with some operations which require kernel help because of unimplemented
+ * native feature and/or instructions in some Meta CPUs. The idea is for
+ * this code to be executed directly in user mode for best efficiency but
+ * which is too intimate with the kernel counter part to be left to user
+ * libraries.  The kernel reserves the right to change this code as needed
+ * without warning. Only the entry points and their results are guaranteed
+ * to be stable.
+ *
+ * Each segment is 64-byte aligned.  This mechanism should be used only for
+ * for things that are really small and justified, and not be abused freely.
+ */
+	.text
+	.global	___user_gateway_start
+___user_gateway_start:
+
+	/* get_tls
+	 * Offset:	 0
+	 * Description:	 Get the TLS pointer for this process.
+	 */
+	.global	___kuser_get_tls
+	.type	___kuser_get_tls,function
+___kuser_get_tls:
+	MOVT	D1Ar1,#HI(USER_GATEWAY_PAGE + USER_GATEWAY_TLS)
+	ADD	D1Ar1,D1Ar1,#LO(USER_GATEWAY_PAGE + USER_GATEWAY_TLS)
+	MOV	D1Ar3,TXENABLE
+	AND	D1Ar3,D1Ar3,#(TXENABLE_THREAD_BITS)
+	LSR	D1Ar3,D1Ar3,#(TXENABLE_THREAD_S - 2)
+	GETD	D0Re0,[D1Ar1+D1Ar3]
+___kuser_get_tls_end:		/* Beyond this point the read will complete */
+	MOV	PC,D1RtP
+	.size	___kuser_get_tls,.-___kuser_get_tls
+	.global	___kuser_get_tls_end
+
+	/* cmpxchg
+	 * Offset:	 64
+	 * Description:  Replace the value at 'ptr' with 'newval' if the current
+	 *		 value is 'oldval'. Return zero if we succeeded,
+	 *		 non-zero otherwise.
+	 *
+	 * Reference prototype:
+	 *
+	 *	int __kuser_cmpxchg(int oldval, int newval, unsigned long *ptr)
+	 *
+	 */
+	.balign 64
+	.global ___kuser_cmpxchg
+	.type   ___kuser_cmpxchg,function
+___kuser_cmpxchg:
+#ifdef CONFIG_SMP
+	/*
+	 * We must use LNKGET/LNKSET with an SMP kernel because the other method
+	 * does not provide atomicity across multiple CPUs.
+	 */
+0:	LNKGETD	D0Re0,[D1Ar3]
+	CMP	D0Re0,D1Ar1
+	LNKSETDZ [D1Ar3],D0Ar2
+	BNZ	1f
+	DEFR	D0Re0,TXSTAT
+	ANDT	D0Re0,D0Re0,#HI(0x3f000000)
+	CMPT	D0Re0,#HI(0x02000000)
+	BNE	0b
+#ifdef CONFIG_LNKGET_AROUND_CACHE
+	DCACHE  [D1Ar3], D0Re0
+#endif
+1:	MOV	D0Re0,#1
+	XORZ	D0Re0,D0Re0,D0Re0
+	MOV	PC,D1RtP
+#else
+	GETD	D0Re0,[D1Ar3]
+	CMP	D0Re0,D1Ar1
+	SETDZ	[D1Ar3],D0Ar2
+___kuser_cmpxchg_end:		/* Beyond this point the write will complete */
+	MOV	D0Re0,#1
+	XORZ	D0Re0,D0Re0,D0Re0
+	MOV	PC,D1RtP
+#endif /* CONFIG_SMP */
+	.size	___kuser_cmpxchg,.-___kuser_cmpxchg
+	.global	___kuser_cmpxchg_end
+
+	.global	___user_gateway_end
+___user_gateway_end:
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 19/40] metag: Scheduling/Process management
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (15 preceding siblings ...)
  2012-10-31 16:13 ` [RFC PATCH v1 18/40] metag: System Calls James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 20/40] metag: Module support James Hogan
                   ` (23 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/thread_info.h |  157 +++++++++++
 arch/metag/kernel/process.c          |  487 ++++++++++++++++++++++++++++++++++
 2 files changed, 644 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/thread_info.h
 create mode 100644 arch/metag/kernel/process.c

diff --git a/arch/metag/include/asm/thread_info.h b/arch/metag/include/asm/thread_info.h
new file mode 100644
index 0000000..7e00282
--- /dev/null
+++ b/arch/metag/include/asm/thread_info.h
@@ -0,0 +1,157 @@
+/* thread_info.h: Meta low-level thread information
+ *
+ * Copyright (C) 2002  David Howells (dhowells@redhat.com)
+ * - Incorporating suggestions made by Linus Torvalds and Dave Miller
+ *
+ * Meta port by Imagination Technologies
+ */
+
+#ifndef _ASM_THREAD_INFO_H
+#define _ASM_THREAD_INFO_H
+
+#ifdef __KERNEL__
+
+#include <linux/compiler.h>
+#include <asm/page.h>
+
+#ifndef __ASSEMBLY__
+#include <asm/processor.h>
+#endif
+
+/*
+ * low level task data that entry.S needs immediate access to
+ * - this struct should fit entirely inside of one cache line
+ * - this struct shares the supervisor stack pages
+ * - if the contents of this structure are changed, the assembly constants must
+ *   also be changed
+ */
+#ifndef __ASSEMBLY__
+
+/* This must be 8 byte aligned so we can ensure stack alignment. */
+struct thread_info {
+	struct task_struct *task;	/* main task structure */
+	struct exec_domain *exec_domain;	/* execution domain */
+	unsigned long flags;	/* low level flags */
+	unsigned long status;	/* thread-synchronous flags */
+	u32 cpu;		/* current CPU */
+	int preempt_count;	/* 0 => preemptable, <0 => BUG */
+
+	mm_segment_t addr_limit;	/* thread address space */
+	struct restart_block restart_block;
+
+	u8 supervisor_stack[0];
+};
+
+#else /* !__ASSEMBLY__ */
+
+#include <generated/asm-offsets.h>
+
+#endif
+
+#define PREEMPT_ACTIVE		0x10000000
+
+#ifdef CONFIG_4KSTACKS
+#define THREAD_SHIFT		12
+#else
+#define THREAD_SHIFT		13
+#endif
+
+#if THREAD_SHIFT >= PAGE_SHIFT
+#define THREAD_SIZE_ORDER	(THREAD_SHIFT - PAGE_SHIFT)
+#else
+#define THREAD_SIZE_ORDER	0
+#endif
+
+#define THREAD_SIZE		(PAGE_SIZE << THREAD_SIZE_ORDER)
+
+#define STACK_WARN		(THREAD_SIZE/8)
+/*
+ * macros/functions for gaining access to the thread information structure
+ */
+#ifndef __ASSEMBLY__
+
+#define INIT_THREAD_INFO(tsk)			\
+{						\
+	.task		= &tsk,			\
+	.exec_domain	= &default_exec_domain,	\
+	.flags		= 0,			\
+	.cpu		= 0,			\
+	.preempt_count	= INIT_PREEMPT_COUNT,	\
+	.addr_limit	= KERNEL_DS,		\
+	.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 current stack pointer from C */
+register unsigned long current_stack_pointer asm("A0StP") __used;
+
+/* how to get the thread information struct from C */
+static inline struct thread_info *current_thread_info(void)
+{
+	return (struct thread_info *)(current_stack_pointer &
+				      ~(THREAD_SIZE - 1));
+}
+
+#define __HAVE_ARCH_KSTACK_END
+static inline int kstack_end(void *addr)
+{
+	return addr == (void *) (((unsigned long) addr & ~(THREAD_SIZE - 1))
+				 + sizeof(struct thread_info));
+}
+
+#endif
+
+/*
+ * thread information flags
+ * - these are process state flags that various assembly files may need to
+ *   access
+ * - pending work-to-be-done flags are in LSW
+ * - other flags in MSW
+ */
+#define TIF_SYSCALL_TRACE	0	/* syscall trace active */
+#define TIF_SIGPENDING		1	/* signal pending */
+#define TIF_NEED_RESCHED	2	/* rescheduling necessary */
+#define TIF_SINGLESTEP		3	/* restore singlestep on return to user
+					   mode */
+#define TIF_SYSCALL_AUDIT	4	/* syscall auditing active */
+#define TIF_SECCOMP		5	/* secure computing */
+#define TIF_RESTORE_SIGMASK	6	/* restore signal mask in do_signal() */
+#define TIF_NOTIFY_RESUME	7	/* callback before returning to user */
+#define TIF_POLLING_NRFLAG      8	/* true if poll_idle() is polling
+					   TIF_NEED_RESCHED */
+#define TIF_MEMDIE		9	/* is terminating due to OOM killer */
+#define TIF_SYSCALL_TRACEPOINT  10	/* syscall tracepoint instrumentation */
+
+
+#define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
+#define _TIF_SIGPENDING		(1<<TIF_SIGPENDING)
+#define _TIF_NEED_RESCHED	(1<<TIF_NEED_RESCHED)
+#define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
+#define _TIF_SYSCALL_AUDIT	(1<<TIF_SYSCALL_AUDIT)
+#define _TIF_SECCOMP		(1<<TIF_SECCOMP)
+#define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
+#define _TIF_RESTORE_SIGMASK	(1<<TIF_RESTORE_SIGMASK)
+#define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
+
+/* work to do in syscall trace */
+#define _TIF_WORK_SYSCALL_MASK	(_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \
+				 _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \
+				 _TIF_SYSCALL_TRACEPOINT)
+
+/* work to do on any return to u-space */
+#define _TIF_ALLWORK_MASK	(_TIF_SYSCALL_TRACE | _TIF_SIGPENDING      | \
+				 _TIF_NEED_RESCHED  | _TIF_SYSCALL_AUDIT   | \
+				 _TIF_SINGLESTEP    | _TIF_RESTORE_SIGMASK | \
+				 _TIF_NOTIFY_RESUME)
+
+/* work to do on interrupt/exception return */
+#define _TIF_WORK_MASK		(_TIF_ALLWORK_MASK & ~(_TIF_SYSCALL_TRACE | \
+				 _TIF_SYSCALL_AUDIT | _TIF_SINGLESTEP))
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_THREAD_INFO_H */
diff --git a/arch/metag/kernel/process.c b/arch/metag/kernel/process.c
new file mode 100644
index 0000000..027e161
--- /dev/null
+++ b/arch/metag/kernel/process.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008,2009,2010,2011 Imagination Technologies
+ *
+ * This file contains the architecture-dependent parts of process handling.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/reboot.h>
+#include <linux/elfcore.h>
+#include <linux/fs.h>
+#include <linux/tick.h>
+#include <linux/slab.h>
+#include <linux/mman.h>
+#include <linux/pm.h>
+#include <linux/uaccess.h>
+#include <asm/hw_breakpoints.h>
+#include <asm/user_gateway.h>
+#include <asm/tcm.h>
+#include <asm/traps.h>
+#include <asm/switch_to.h>
+
+void cpu_idle(void)
+{
+	set_thread_flag(TIF_POLLING_NRFLAG);
+
+	while (1) {
+		tick_nohz_idle_enter();
+		rcu_idle_enter();
+
+		while (!need_resched()) {
+#ifdef CONFIG_HOTPLUG_CPU
+			if (cpu_is_offline(smp_processor_id()))
+				cpu_die();
+#endif
+			__asm__ volatile ("SWITCH #0x400000\n"
+					  : : : "memory");
+		}
+
+		rcu_idle_exit();
+		tick_nohz_idle_exit();
+		schedule_preempt_disabled();
+	 }
+}
+
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+
+void (*soc_restart)(char *cmd);
+void (*soc_halt)(void);
+
+void machine_restart(char *cmd)
+{
+	if (soc_restart)
+		soc_restart(cmd);
+	hard_processor_halt(HALT_OK);
+}
+
+void machine_halt(void)
+{
+	if (soc_halt)
+		soc_halt();
+	smp_send_stop();
+	hard_processor_halt(HALT_OK);
+}
+
+void machine_power_off(void)
+{
+	if (pm_power_off)
+		pm_power_off();
+	smp_send_stop();
+	hard_processor_halt(HALT_OK);
+}
+
+#define FLAG_Z 0x8
+#define FLAG_N 0x4
+#define FLAG_O 0x2
+#define FLAG_C 0x1
+
+void show_regs(struct pt_regs *regs)
+{
+	int i;
+	const char *AX0_names[] = {"A0StP", "A0FrP"};
+	const char *AX1_names[] = {"A1GbP", "A1LbP"};
+
+	const char *DX0_names[] = {
+		"D0Re0",
+		"D0Ar6",
+		"D0Ar4",
+		"D0Ar2",
+		"D0FrT",
+		"D0.5 ",
+		"D0.6 ",
+		"D0.7 "
+	};
+
+	const char *DX1_names[] = {
+		"D1Re0",
+		"D1Ar5",
+		"D1Ar3",
+		"D1Ar1",
+		"D1RtP",
+		"D1.5 ",
+		"D1.6 ",
+		"D1.7 "
+	};
+
+	pr_info(" pt_regs @ %p\n", regs);
+	pr_info(" SaveMask = 0x%04hx\n", regs->ctx.SaveMask);
+	pr_info(" Flags = 0x%04hx (%c%c%c%c)\n", regs->ctx.Flags,
+		regs->ctx.Flags & FLAG_Z ? 'Z' : 'z',
+		regs->ctx.Flags & FLAG_N ? 'N' : 'n',
+		regs->ctx.Flags & FLAG_O ? 'O' : 'o',
+		regs->ctx.Flags & FLAG_C ? 'C' : 'c');
+	pr_info(" TXRPT = 0x%08x\n", regs->ctx.CurrRPT);
+	pr_info(" PC = 0x%08x\n", regs->ctx.CurrPC);
+
+	/* AX regs */
+	for (i = 0; i < 2; i++) {
+		pr_info(" %s = 0x%08x    ",
+			AX0_names[i],
+			regs->ctx.AX[i].U0);
+		printk(" %s = 0x%08x\n",
+			AX1_names[i],
+			regs->ctx.AX[i].U1);
+	}
+
+	if (regs->ctx.SaveMask & TBICTX_XEXT_BIT)
+		pr_warn(" Extended state present - AX2.[01] will be WRONG\n");
+
+	/* Special place with AXx.2 */
+	pr_info(" A0.2  = 0x%08x    ",
+		regs->ctx.Ext.AX2.U0);
+	printk(" A1.2  = 0x%08x\n",
+		regs->ctx.Ext.AX2.U1);
+
+	/* 'extended' AX regs (nominally, just AXx.3) */
+	for (i = 0; i < (TBICTX_AX_REGS - 3); i++) {
+		pr_info(" A0.%d  = 0x%08x    ", i + 3, regs->ctx.AX3[i].U0);
+		printk(" A1.%d  = 0x%08x\n", i + 3, regs->ctx.AX3[i].U1);
+	}
+
+	for (i = 0; i < 8; i++) {
+		pr_info(" %s = 0x%08x    ", DX0_names[i], regs->ctx.DX[i].U0);
+		printk(" %s = 0x%08x\n", DX1_names[i], regs->ctx.DX[i].U1);
+	}
+
+	show_trace(NULL, (unsigned long *)regs->ctx.AX[0].U0, regs);
+}
+
+extern void kernel_thread_helper(void);
+__asm__(".pushsection .text\n\t"
+	".type _kernel_thread_helper,function\n\t"
+	"_kernel_thread_helper:\n\t"
+	"SWAP    PC,D1RtP\n\t"
+	"MOV     D1Ar1,D0Re0\n\t"
+	"MOVT    D1RtP,#HI(_do_exit)\n\t"
+	"CALL    D1RtP,#LO(_do_exit)\n\t"
+	".size   _kernel_thread_helper,.-_kernel_thread_helper\n\t"
+	".popsection");
+
+/*
+ * Create a kernel thread
+ */
+int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+{
+	struct pt_regs regs;
+	unsigned long global_base;
+
+	memset(&regs, 0, sizeof(regs));
+	regs.ctx.DX[3].U1 = (unsigned long)arg;
+	regs.ctx.DX[4].U1 = (unsigned long)fn;
+
+	asm volatile("MOV %0,A1GbP\n" : "=r" (global_base));
+	regs.ctx.AX[0].U1 = (unsigned long)global_base;
+
+	regs.ctx.CurrPC = (unsigned long)kernel_thread_helper;
+
+	/* Ok, create the new process.. */
+	return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,
+		       &regs, 0, NULL, NULL);
+}
+
+int copy_thread(unsigned long clone_flags, unsigned long usp,
+		unsigned long unused, struct task_struct *tsk,
+		struct pt_regs *regs)
+{
+	struct pt_regs *childregs = task_pt_regs(tsk);
+	void *kernel_context = ((void *) childregs +
+				sizeof(struct pt_regs));
+
+	BUG_ON(((unsigned long)childregs) & 0x7);
+	BUG_ON(((unsigned long)kernel_context) & 0x7);
+
+	/* Get a pointer to where the new child's register block should have
+	 * been pushed.
+	 * The Meta's stack grows upwards, and the context is the the first
+	 * thing to be pushed by TBX (phew)
+	 */
+	*childregs = *regs;
+
+	/* Set the correct stack for the clone mode */
+	if (user_mode(childregs)) {
+		childregs->ctx.AX[0].U0 = ALIGN(usp, 8);
+		tsk->thread.int_depth = 1;
+	} else {
+		childregs->ctx.AX[0].U0 = (unsigned long) kernel_context;
+		tsk->thread.int_depth = 2;
+	}
+
+	/* set return value for child process */
+	childregs->ctx.DX[0].U0 = 0;
+
+	tsk->thread.kernel_context = __TBISwitchInit(kernel_context,
+						     ret_from_fork,
+						     0, 0);
+
+	/* The TLS pointer is passed as an argument to sys_clone. */
+	if (clone_flags & CLONE_SETTLS)
+		tsk->thread.tls_ptr = (__force void __user *)regs->ctx.DX[1].U1;
+
+#ifdef CONFIG_META_FPU
+	if (tsk->thread.fpu_context) {
+		struct meta_fpu_context *ctx;
+
+		ctx = kmemdup(tsk->thread.fpu_context,
+			      sizeof(struct meta_fpu_context), GFP_ATOMIC);
+		tsk->thread.fpu_context = ctx;
+	}
+#endif
+
+#ifdef CONFIG_META_DSP
+	if (tsk->thread.dsp_context) {
+		struct meta_ext_context *ctx;
+		int i;
+
+		ctx = kmemdup(tsk->thread.dsp_context,
+			      sizeof(struct meta_ext_context), GFP_ATOMIC);
+		for (i = 0; i < 2; i++)
+			ctx->ram[i] = kmemdup(ctx->ram[i], ctx->ram_sz[i],
+					      GFP_ATOMIC);
+		tsk->thread.dsp_context = ctx;
+	}
+#endif
+
+	return 0;
+}
+
+#ifdef CONFIG_META_FPU
+static void alloc_fpu_context(struct thread_struct *thread)
+{
+	thread->fpu_context = kzalloc(sizeof(struct meta_fpu_context),
+				      GFP_ATOMIC);
+}
+
+static void clear_fpu(struct thread_struct *thread)
+{
+	thread->user_flags &= ~TBICTX_FPAC_BIT;
+	kfree(thread->fpu_context);
+	thread->fpu_context = NULL;
+}
+#else
+static void clear_fpu(struct thread_struct *thread)
+{
+}
+#endif
+
+#ifdef CONFIG_META_DSP
+static void clear_dsp(struct thread_struct *thread)
+{
+	if (thread->dsp_context) {
+		kfree(thread->dsp_context->ram[0]);
+		kfree(thread->dsp_context->ram[1]);
+
+		kfree(thread->dsp_context);
+
+		thread->dsp_context = NULL;
+	}
+
+	TBI_SETREG(D0.8, 0);
+}
+#else
+static void clear_dsp(struct thread_struct *thread)
+{
+}
+#endif
+
+struct task_struct *__sched __switch_to(struct task_struct *prev,
+					struct task_struct *next)
+{
+	TBIRES to, from;
+
+	to.Switch.pCtx = next->thread.kernel_context;
+	to.Switch.pPara = prev;
+
+#ifdef CONFIG_META_FPU
+	if (prev->thread.user_flags & TBICTX_FPAC_BIT) {
+		struct pt_regs *regs = task_pt_regs(prev);
+		TBIRES state;
+
+		state.Sig.SaveMask = prev->thread.user_flags;
+		state.Sig.pCtx = &regs->ctx;
+
+		if (!prev->thread.fpu_context)
+			alloc_fpu_context(&prev->thread);
+		if (prev->thread.fpu_context)
+			__TBICtxFPUSave(state, prev->thread.fpu_context);
+	}
+	/*
+	 * Force a restore of the FPU context next time this process is
+	 * scheduled.
+	 */
+	if (prev->thread.fpu_context)
+		prev->thread.fpu_context->needs_restore = true;
+#endif
+
+
+	if (current->thread.hwbp_context &&
+		(current->thread.hwbp_context->written & META_HWBP_WRITTEN))
+		restore_hwbp_controller(current->thread.hwbp_context);
+
+	from = __TBISwitch(to, &prev->thread.kernel_context);
+
+	/* Restore TLS pointer for this process. */
+	set_gateway_tls(current->thread.tls_ptr);
+
+	if (current->thread.hwbp_context)
+		setup_hwbp_controller(current->thread.hwbp_context);
+
+	return (struct task_struct *) from.Switch.pPara;
+}
+
+int sys_clone(unsigned long clone_flags,
+	      unsigned long newsp,
+	      unsigned long parent_tidptr,
+	      unsigned long child_tidptr,
+	      unsigned long tls,
+	      unsigned long arg6,
+	      struct pt_regs *pregs)
+{
+	if (!newsp)
+		newsp = pregs->ctx.AX[0].U0;
+
+	return do_fork(clone_flags, newsp, pregs, 0,
+		       (int __user *)parent_tidptr,
+		       (int __user *)child_tidptr);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+int sys_execve(char __user *ufilename,
+	       const char __user *__user *uargv,
+	       const char __user *__user *uenvp,
+	       unsigned long arg4,
+	       unsigned long arg5,
+	       unsigned long arg6,
+	       struct pt_regs *regs)
+{
+	int error;
+	char *filename;
+
+	filename = getname(ufilename);
+
+	error = PTR_ERR(filename);
+	if (IS_ERR(filename))
+		goto out;
+
+	error = do_execve(filename, uargv, uenvp, regs);
+	putname(filename);
+out:
+	return error;
+}
+
+void flush_thread(void)
+{
+	clear_fpu(&current->thread);
+	clear_dsp(&current->thread);
+}
+
+/*
+ * Free current thread data structures etc.
+ */
+void exit_thread(void)
+{
+	clear_fpu(&current->thread);
+	clear_dsp(&current->thread);
+}
+
+/* TODO: figure out how to unwind the kernel stack here to figure out
+ * where we went to sleep. */
+unsigned long get_wchan(struct task_struct *p)
+{
+	return 0;
+}
+
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+	/* Returning 0 indicates that the FPU state was not stored (as it was
+	 * not in use) */
+	return 0;
+}
+
+#ifdef CONFIG_META_USER_TCM
+
+#define ELF_MIN_ALIGN	PAGE_SIZE
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))
+#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))
+
+#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE)
+
+unsigned long __metag_elf_map(struct file *filep, unsigned long addr,
+			      struct elf_phdr *eppnt, int prot, int type,
+			      unsigned long total_size)
+{
+	unsigned long map_addr, size;
+	unsigned long page_off = ELF_PAGEOFFSET(eppnt->p_vaddr);
+	unsigned long raw_size = eppnt->p_filesz + page_off;
+	unsigned long off = eppnt->p_offset - page_off;
+	unsigned int tcm_tag;
+	addr = ELF_PAGESTART(addr);
+	size = ELF_PAGEALIGN(raw_size);
+
+	/* mmap() will return -EINVAL if given a zero size, but a
+	 * segment with zero filesize is perfectly valid */
+	if (!size)
+		return addr;
+
+	tcm_tag = tcm_lookup_tag(addr);
+
+	if (tcm_tag != TCM_INVALID_TAG)
+		type &= ~MAP_FIXED;
+
+	/*
+	* total_size is the size of the ELF (interpreter) image.
+	* The _first_ mmap needs to know the full size, otherwise
+	* randomization might put this image into an overlapping
+	* position with the ELF binary image. (since size < total_size)
+	* So we first map the 'big' image - and unmap the remainder at
+	* the end. (which unmap is needed for ELF images with holes.)
+	*/
+	if (total_size) {
+		total_size = ELF_PAGEALIGN(total_size);
+		map_addr = vm_mmap(filep, addr, total_size, prot, type, off);
+		if (!BAD_ADDR(map_addr))
+			vm_munmap(map_addr+size, total_size-size);
+	} else
+		map_addr = vm_mmap(filep, addr, size, prot, type, off);
+
+	if (!BAD_ADDR(map_addr) && tcm_tag != TCM_INVALID_TAG) {
+		struct tcm_allocation *tcm;
+		unsigned long tcm_addr;
+
+		tcm = kmalloc(sizeof(*tcm), GFP_KERNEL);
+		if (!tcm)
+			return -ENOMEM;
+
+		tcm_addr = tcm_alloc(tcm_tag, raw_size);
+		if (tcm_addr != addr) {
+			kfree(tcm);
+			return -ENOMEM;
+		}
+
+		tcm->tag = tcm_tag;
+		tcm->addr = tcm_addr;
+		tcm->size = raw_size;
+
+		list_add(&tcm->list, &current->mm->context.tcm);
+
+		eppnt->p_vaddr = map_addr;
+		if (copy_from_user((void *) addr, (void __user *) map_addr,
+				   raw_size))
+			return -EFAULT;
+	}
+
+	return map_addr;
+}
+#endif
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 20/40] metag: Module support
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (16 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 19/40] metag: Scheduling/Process management James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 21/40] metag: Atomics, locks and bitops James Hogan
                   ` (22 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/module.h |   39 ++++++
 arch/metag/kernel/metag_ksyms.c |    4 +
 arch/metag/kernel/module.c      |  284 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 327 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/module.h
 create mode 100644 arch/metag/kernel/module.c

diff --git a/arch/metag/include/asm/module.h b/arch/metag/include/asm/module.h
new file mode 100644
index 0000000..e8e3a28
--- /dev/null
+++ b/arch/metag/include/asm/module.h
@@ -0,0 +1,39 @@
+#ifndef _ASM_METAG_MODULE_H
+#define _ASM_METAG_MODULE_H
+
+struct metag_plt_entry {
+	/* Indirect jump instruction sequence. */
+	unsigned long tramp[2];
+};
+
+struct mod_arch_specific {
+	/* Indices of PLT sections within module. */
+	unsigned int core_plt_section, init_plt_section;
+};
+
+#if defined CONFIG_META12
+#define MODULE_PROC_FAMILY "META 1.2 "
+#elif defined CONFIG_META21
+#define MODULE_PROC_FAMILY "META 2.1 "
+#else
+#define MODULE_PROC_FAMILY ""
+#endif
+
+#ifdef CONFIG_4KSTACKS
+#define MODULE_STACKSIZE "4KSTACKS "
+#else
+#define MODULE_STACKSIZE ""
+#endif
+
+#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY MODULE_STACKSIZE
+
+#define Elf_Shdr Elf32_Shdr
+#define Elf_Sym Elf32_Sym
+#define Elf_Ehdr Elf32_Ehdr
+
+#ifdef MODULE
+asm(".section .plt,\"ax\",@progbits; .balign 8; .previous");
+asm(".section .init.plt,\"ax\",@progbits; .balign 8; .previous");
+#endif
+
+#endif /* _ASM_METAG_MODULE_H */
diff --git a/arch/metag/kernel/metag_ksyms.c b/arch/metag/kernel/metag_ksyms.c
index 0ea1332..7b64076 100644
--- a/arch/metag/kernel/metag_ksyms.c
+++ b/arch/metag/kernel/metag_ksyms.c
@@ -66,6 +66,10 @@ DECLARE_EXPORT(__umodsi3);
 DECLARE_EXPORT(__modsi3);
 DECLARE_EXPORT(__muldi3);
 
+/* Maths functions */
+EXPORT_SYMBOL(div_u64);
+EXPORT_SYMBOL(div_s64);
+
 /* String functions */
 EXPORT_SYMBOL(memcpy);
 EXPORT_SYMBOL(memset);
diff --git a/arch/metag/kernel/module.c b/arch/metag/kernel/module.c
new file mode 100644
index 0000000..986331c
--- /dev/null
+++ b/arch/metag/kernel/module.c
@@ -0,0 +1,284 @@
+/*  Kernel module help for Meta.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+*/
+#include <linux/moduleloader.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sort.h>
+
+#include <asm/unaligned.h>
+
+/* Count how many different relocations (different symbol, different
+   addend) */
+static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num)
+{
+	unsigned int i, r_info, r_addend, _count_relocs;
+
+	_count_relocs = 0;
+	r_info = 0;
+	r_addend = 0;
+	for (i = 0; i < num; i++)
+		/* Only count relbranch relocs, others don't need stubs */
+		if (ELF32_R_TYPE(rela[i].r_info) == R_METAG_RELBRANCH &&
+		    (r_info != ELF32_R_SYM(rela[i].r_info) ||
+		     r_addend != rela[i].r_addend)) {
+			_count_relocs++;
+			r_info = ELF32_R_SYM(rela[i].r_info);
+			r_addend = rela[i].r_addend;
+		}
+
+	return _count_relocs;
+}
+
+static int relacmp(const void *_x, const void *_y)
+{
+	const Elf32_Rela *x, *y;
+
+	y = (Elf32_Rela *)_x;
+	x = (Elf32_Rela *)_y;
+
+	/* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to
+	 * make the comparison cheaper/faster. It won't affect the sorting or
+	 * the counting algorithms' performance
+	 */
+	if (x->r_info < y->r_info)
+		return -1;
+	else if (x->r_info > y->r_info)
+		return 1;
+	else if (x->r_addend < y->r_addend)
+		return -1;
+	else if (x->r_addend > y->r_addend)
+		return 1;
+	else
+		return 0;
+}
+
+static void relaswap(void *_x, void *_y, int size)
+{
+	uint32_t *x, *y, tmp;
+	int i;
+
+	y = (uint32_t *)_x;
+	x = (uint32_t *)_y;
+
+	for (i = 0; i < sizeof(Elf32_Rela) / sizeof(uint32_t); i++) {
+		tmp = x[i];
+		x[i] = y[i];
+		y[i] = tmp;
+	}
+}
+
+/* Get the potential trampolines size required of the init and
+   non-init sections */
+static unsigned long get_plt_size(const Elf32_Ehdr *hdr,
+				  const Elf32_Shdr *sechdrs,
+				  const char *secstrings,
+				  int is_init)
+{
+	unsigned long ret = 0;
+	unsigned i;
+
+	/* Everything marked ALLOC (this includes the exported
+	   symbols) */
+	for (i = 1; i < hdr->e_shnum; i++) {
+		/* If it's called *.init*, and we're not init, we're
+		   not interested */
+		if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != NULL)
+		    != is_init)
+			continue;
+
+		/* We don't want to look at debug sections. */
+		if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != NULL)
+			continue;
+
+		if (sechdrs[i].sh_type == SHT_RELA) {
+			pr_debug("Found relocations in section %u\n", i);
+			pr_debug("Ptr: %p.  Number: %u\n",
+				 (void *)hdr + sechdrs[i].sh_offset,
+				 sechdrs[i].sh_size / sizeof(Elf32_Rela));
+
+			/* Sort the relocation information based on a symbol and
+			 * addend key. This is a stable O(n*log n) complexity
+			 * alogrithm but it will reduce the complexity of
+			 * count_relocs() to linear complexity O(n)
+			 */
+			sort((void *)hdr + sechdrs[i].sh_offset,
+			     sechdrs[i].sh_size / sizeof(Elf32_Rela),
+			     sizeof(Elf32_Rela), relacmp, relaswap);
+
+			ret += count_relocs((void *)hdr
+					     + sechdrs[i].sh_offset,
+					     sechdrs[i].sh_size
+					     / sizeof(Elf32_Rela))
+				* sizeof(struct metag_plt_entry);
+		}
+	}
+
+	return ret;
+}
+
+int module_frob_arch_sections(Elf32_Ehdr *hdr,
+			      Elf32_Shdr *sechdrs,
+			      char *secstrings,
+			      struct module *me)
+{
+	unsigned int i;
+
+	/* Find .plt and .init.plt sections */
+	for (i = 0; i < hdr->e_shnum; i++) {
+		if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0)
+			me->arch.init_plt_section = i;
+		else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0)
+			me->arch.core_plt_section = i;
+	}
+	if (!me->arch.core_plt_section || !me->arch.init_plt_section) {
+		pr_err("Module doesn't contain .plt or .init.plt sections.\n");
+		return -ENOEXEC;
+	}
+
+	/* Override their sizes */
+	sechdrs[me->arch.core_plt_section].sh_size
+		= get_plt_size(hdr, sechdrs, secstrings, 0);
+	sechdrs[me->arch.core_plt_section].sh_type = SHT_NOBITS;
+	sechdrs[me->arch.init_plt_section].sh_size
+		= get_plt_size(hdr, sechdrs, secstrings, 1);
+	sechdrs[me->arch.init_plt_section].sh_type = SHT_NOBITS;
+	return 0;
+}
+
+/* Set up a trampoline in the PLT to bounce us to the distant function */
+static uint32_t do_plt_call(void *location, Elf32_Addr val,
+			    Elf32_Shdr *sechdrs, struct module *mod)
+{
+	struct metag_plt_entry *entry;
+	/* Instructions used to do the indirect jump.  */
+	uint32_t tramp[2];
+
+	/* We have to trash a register, so we assume that any control
+	   transfer more than 21-bits away must be a function call
+	   (so we can use a call-clobbered register).  */
+
+	/* MOVT D0Re0,#HI(v) */
+	tramp[0] = 0x02000005 | (((val & 0xffff0000) >> 16) << 3);
+	/* JUMP D0Re0,#LO(v) */
+	tramp[1] = 0xac000001 | ((val & 0x0000ffff) << 3);
+
+	/* Init, or core PLT? */
+	if (location >= mod->module_core
+	    && location < mod->module_core + mod->core_size)
+		entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
+	else
+		entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
+
+	/* Find this entry, or if that fails, the next avail. entry */
+	while (entry->tramp[0])
+		if (entry->tramp[0] == tramp[0] && entry->tramp[1] == tramp[1])
+			return (uint32_t)entry;
+		else
+			entry++;
+
+	entry->tramp[0] = tramp[0];
+	entry->tramp[1] = tramp[1];
+
+	return (uint32_t)entry;
+}
+
+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;
+	Elf32_Addr relocation;
+	uint32_t *location;
+	int32_t value;
+
+	pr_debug("Applying relocate section %u to %u\n", relsec,
+		 sechdrs[relsec].sh_info);
+	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+		/* 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);
+		relocation = sym->st_value + rel[i].r_addend;
+
+		switch (ELF32_R_TYPE(rel[i].r_info)) {
+		case R_METAG_NONE:
+			break;
+		case R_METAG_HIADDR16:
+			relocation >>= 16;
+		case R_METAG_LOADDR16:
+			*location = (*location & 0xfff80007) |
+				((relocation & 0xffff) << 3);
+			break;
+		case R_METAG_ADDR32:
+			/*
+			 * Packed data structures may cause a misaligned
+			 * R_METAG_ADDR32 to be emitted.
+			 */
+			put_unaligned(relocation, location);
+			break;
+		case R_METAG_GETSETOFF:
+			*location += ((relocation & 0xfff) << 7);
+			break;
+		case R_METAG_RELBRANCH:
+			if (*location & (0x7ffff << 5)) {
+				pr_err("bad relbranch relocation\n");
+				break;
+			}
+
+			/* This jump is too big for the offset slot. Build
+			 * a PLT to jump through to get to where we want to go.
+			 * NB: 21bit check - not scaled to 19bit yet
+			 */
+			if (((int32_t)(relocation -
+				       (uint32_t)location) > 0xfffff) ||
+			    ((int32_t)(relocation -
+				       (uint32_t)location) < -0xfffff)) {
+				relocation = do_plt_call(location, relocation,
+							 sechdrs, me);
+			}
+
+			value = relocation - (uint32_t)location;
+
+			/* branch instruction aligned */
+			value /= 4;
+
+			if ((value > 0x7ffff) || (value < -0x7ffff)) {
+				/*
+				 * this should have been caught by the code
+				 * above!
+				 */
+				pr_err("overflow of relbranch reloc\n");
+			}
+
+			*location = (*location & (~(0x7ffff << 5))) |
+				((value & 0x7ffff) << 5);
+			break;
+
+		default:
+			pr_err("module %s: Unknown relocation: %u\n",
+			       me->name, ELF32_R_TYPE(rel[i].r_info));
+			return -ENOEXEC;
+		}
+	}
+	return 0;
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 21/40] metag: Atomics, locks and bitops
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (17 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 20/40] metag: Module support James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 22/40] metag: Basic documentation James Hogan
                   ` (21 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add header files to implement Meta hardware thread locks (used by some
other atomic operations), atomics, spinlocks, and bitops.

There are 2 main types of atomic primitives on Meta (in addition to IRQs
off on UP):
 - LOCK instructions provide locking between hardware threads.
 - LNKGET/LNKSET instructions provide load-linked/store-conditional
   operations allowing for lighter weight atomics on Meta2)

LOCK instructions allow for hardware threads to acquire voluntary or
exclusive hardware thread locks:
 - LOCK0 releases exclusive and voluntary lock from the running hardware
   thread.
 - LOCK1 acquires the voluntary hardware lock, blocking until it becomes
   available.
 - LOCK2 implies LOCK1, and additionally acquires the exclusive hardware
   lock, blocking all other hardware threads from executing.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/atomic.h          |   53 ++++++
 arch/metag/include/asm/atomic_lnkget.h   |  245 ++++++++++++++++++++++++++++
 arch/metag/include/asm/atomic_lock1.h    |  160 ++++++++++++++++++
 arch/metag/include/asm/bitops.h          |  135 +++++++++++++++
 arch/metag/include/asm/cmpxchg.h         |   65 ++++++++
 arch/metag/include/asm/cmpxchg_irq.h     |   42 +++++
 arch/metag/include/asm/cmpxchg_lnkget.h  |   86 ++++++++++
 arch/metag/include/asm/cmpxchg_lock1.h   |   48 ++++++
 arch/metag/include/asm/lock.h            |   17 ++
 arch/metag/include/asm/spinlock.h        |   22 +++
 arch/metag/include/asm/spinlock_lnkget.h |  261 ++++++++++++++++++++++++++++++
 arch/metag/include/asm/spinlock_lock1.h  |  184 +++++++++++++++++++++
 arch/metag/include/asm/spinlock_types.h  |   20 +++
 13 files changed, 1338 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/atomic.h
 create mode 100644 arch/metag/include/asm/atomic_lnkget.h
 create mode 100644 arch/metag/include/asm/atomic_lock1.h
 create mode 100644 arch/metag/include/asm/bitops.h
 create mode 100644 arch/metag/include/asm/cmpxchg.h
 create mode 100644 arch/metag/include/asm/cmpxchg_irq.h
 create mode 100644 arch/metag/include/asm/cmpxchg_lnkget.h
 create mode 100644 arch/metag/include/asm/cmpxchg_lock1.h
 create mode 100644 arch/metag/include/asm/lock.h
 create mode 100644 arch/metag/include/asm/spinlock.h
 create mode 100644 arch/metag/include/asm/spinlock_lnkget.h
 create mode 100644 arch/metag/include/asm/spinlock_lock1.h
 create mode 100644 arch/metag/include/asm/spinlock_types.h

diff --git a/arch/metag/include/asm/atomic.h b/arch/metag/include/asm/atomic.h
new file mode 100644
index 0000000..f198759
--- /dev/null
+++ b/arch/metag/include/asm/atomic.h
@@ -0,0 +1,53 @@
+#ifndef __ASM_METAG_ATOMIC_H
+#define __ASM_METAG_ATOMIC_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <asm/cmpxchg.h>
+
+#if defined(CONFIG_ATOMICITY_IRQSOFF)
+/* The simple UP case. */
+#include <asm-generic/atomic.h>
+#else
+
+#if defined(CONFIG_ATOMICITY_LOCK1)
+#include <asm/atomic_lock1.h>
+#else
+#include <asm/atomic_lnkget.h>
+#endif
+
+#define atomic_add_negative(a, v)       (atomic_add_return((a), (v)) < 0)
+
+#define atomic_dec_return(v) atomic_sub_return(1, (v))
+#define atomic_inc_return(v) atomic_add_return(1, (v))
+
+/*
+ * atomic_inc_and_test - increment and test
+ * @v: pointer of type atomic_t
+ *
+ * Atomically increments @v by 1
+ * and returns true if the result is zero, or false for all
+ * other cases.
+ */
+#define atomic_inc_and_test(v) (atomic_inc_return(v) == 0)
+
+#define atomic_sub_and_test(i, v) (atomic_sub_return((i), (v)) == 0)
+#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
+
+#define atomic_inc(v) atomic_add(1, (v))
+#define atomic_dec(v) atomic_sub(1, (v))
+
+#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
+
+#define smp_mb__before_atomic_dec()	barrier()
+#define smp_mb__after_atomic_dec()	barrier()
+#define smp_mb__before_atomic_inc()	barrier()
+#define smp_mb__after_atomic_inc()	barrier()
+
+#endif
+
+#define atomic_dec_if_positive(v)       atomic_sub_if_positive(1, v)
+
+#include <asm-generic/atomic64.h>
+
+#endif /* __ASM_METAG_ATOMIC_H */
diff --git a/arch/metag/include/asm/atomic_lnkget.h b/arch/metag/include/asm/atomic_lnkget.h
new file mode 100644
index 0000000..b05be05
--- /dev/null
+++ b/arch/metag/include/asm/atomic_lnkget.h
@@ -0,0 +1,245 @@
+#ifndef __ASM_METAG_ATOMIC_LNKGET_H
+#define __ASM_METAG_ATOMIC_LNKGET_H
+
+#define ATOMIC_INIT(i)	{ (i) }
+
+#define atomic_set(v, i)		((v)->counter = (i))
+
+#include <linux/compiler.h>
+
+#include <asm/barrier.h>
+
+/*
+ * None of these asm statements clobber memory as LNKSET writes around
+ * the cache so the memory it modifies cannot safely be read by any means
+ * other than these accessors.
+ */
+
+static inline int atomic_read(const atomic_t *v)
+{
+	int temp;
+
+	__asm__ __volatile__(
+		"LNKGETD %0, [%1]\n"
+		: "=da" (temp)
+		: "da" (&v->counter)
+	);
+
+	return temp;
+}
+
+static inline void atomic_add(int i, atomic_t *v)
+{
+	int temp;
+
+	__asm__ __volatile__(
+		"1:	LNKGETD %0, [%1]\n"
+		"	ADD	%0, %0, %2\n"
+		"	LNKSETD [%1], %0\n"
+		"	DEFR	%0, TXSTAT\n"
+		"	ANDT	%0, %0, #HI(0x3f000000)\n"
+		"	CMPT	%0, #HI(0x02000000)\n"
+		"	BNZ	1b\n"
+		: "=&d" (temp)
+		: "da" (&v->counter), "bd" (i)
+		: "cc"
+	);
+}
+
+static inline void atomic_sub(int i, atomic_t *v)
+{
+	int temp;
+
+	__asm__ __volatile__(
+		"1:	LNKGETD %0, [%1]\n"
+		"	SUB	%0, %0, %2\n"
+		"	LNKSETD [%1], %0\n"
+		"	DEFR	%0, TXSTAT\n"
+		"	ANDT	%0, %0, #HI(0x3f000000)\n"
+		"	CMPT	%0, #HI(0x02000000)\n"
+		"	BNZ 1b\n"
+		: "=&d" (temp)
+		: "da" (&v->counter), "bd" (i)
+		: "cc"
+	);
+}
+
+static inline int atomic_add_return(int i, atomic_t *v)
+{
+	int result, temp;
+
+	smp_mb();
+
+	__asm__ __volatile__(
+		"1:	LNKGETD %1, [%2]\n"
+		"	ADD	%1, %1, %3\n"
+		"	LNKSETD [%2], %1\n"
+		"	DEFR	%0, TXSTAT\n"
+		"	ANDT	%0, %0, #HI(0x3f000000)\n"
+		"	CMPT	%0, #HI(0x02000000)\n"
+		"	BNZ 1b\n"
+		: "=&d" (temp), "=&da" (result)
+		: "da" (&v->counter), "bd" (i)
+		: "cc"
+	);
+
+	smp_mb();
+
+	return result;
+}
+
+static inline int atomic_sub_return(int i, atomic_t *v)
+{
+	int result, temp;
+
+	smp_mb();
+
+	__asm__ __volatile__(
+		"1:	LNKGETD %1, [%2]\n"
+		"	SUB	%1, %1, %3\n"
+		"	LNKSETD [%2], %1\n"
+		"	DEFR	%0, TXSTAT\n"
+		"	ANDT	%0, %0, #HI(0x3f000000)\n"
+		"	CMPT	%0, #HI(0x02000000)\n"
+		"	BNZ	1b\n"
+		: "=&d" (temp), "=&da" (result)
+		: "da" (&v->counter), "bd" (i)
+		: "cc"
+	);
+
+	smp_mb();
+
+	return result;
+}
+
+static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
+{
+	int temp;
+
+	__asm__ __volatile__(
+		"1:	LNKGETD %0, [%1]\n"
+		"	AND	%0, %0, %2\n"
+		"	LNKSETD	[%1] %0\n"
+		"	DEFR	%0, TXSTAT\n"
+		"	ANDT	%0, %0, #HI(0x3f000000)\n"
+		"	CMPT	%0, #HI(0x02000000)\n"
+		"	BNZ	1b\n"
+		: "=&d" (temp)
+		: "da" (&v->counter), "bd" (~mask)
+		: "cc"
+	);
+}
+
+static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
+{
+	int temp;
+
+	__asm__ __volatile__(
+		"1:	LNKGETD %0, [%1]\n"
+		"	OR	%0, %0, %2\n"
+		"	LNKSETD	[%1], %0\n"
+		"	DEFR	%0, TXSTAT\n"
+		"	ANDT	%0, %0, #HI(0x3f000000)\n"
+		"	CMPT	%0, #HI(0x02000000)\n"
+		"	BNZ	1b\n"
+		: "=&d" (temp)
+		: "da" (&v->counter), "bd" (mask)
+		: "cc"
+	);
+}
+
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+	int result, temp;
+
+	smp_mb();
+
+	__asm__ __volatile__(
+		"1:	LNKGETD	%1, [%2]\n"
+		"	CMP	%1, %3\n"
+		"	LNKSETDEQ [%2], %4\n"
+		"	BNE	2f\n"
+		"	DEFR	%0, TXSTAT\n"
+		"	ANDT	%0, %0, #HI(0x3f000000)\n"
+		"	CMPT	%0, #HI(0x02000000)\n"
+		"	BNZ	1b\n"
+		"2:\n"
+		: "=&d" (temp), "=&d" (result)
+		: "da" (&v->counter), "bd" (old), "da" (new)
+		: "cc"
+	);
+
+	smp_mb();
+
+	return result;
+}
+
+static inline int atomic_xchg(atomic_t *v, int new)
+{
+	int temp, old;
+
+	__asm__ __volatile__(
+			"1:	LNKGETD %1, [%2]\n"
+			"	LNKSETD	[%2], %3\n"
+			"	DEFR	%0, TXSTAT\n"
+			"	ANDT	%0, %0, #HI(0x3f000000)\n"
+			"	CMPT	%0, #HI(0x02000000)\n"
+			"	BNZ	1b\n"
+			: "=&d" (temp), "=&d" (old)
+			: "da" (&v->counter), "da" (new)
+			: "cc"
+	);
+
+	return old;
+}
+
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
+{
+	int result, temp;
+
+	smp_mb();
+
+	__asm__ __volatile__(
+			"1:	LNKGETD %1, [%2]\n"
+			"	CMP	%1, %3\n"
+			"	ADD	%0, %1, %4\n"
+			"	LNKSETDNE [%2], %0\n"
+			"	BEQ	2f\n"
+			"	DEFR	%0, TXSTAT\n"
+			"	ANDT	%0, %0, #HI(0x3f000000)\n"
+			"	CMPT	%0, #HI(0x02000000)\n"
+			"	BNZ	1b\n"
+			"2:\n"
+			: "=&d" (temp), "=&d" (result)
+			: "da" (&v->counter), "bd" (u), "bd" (a)
+			: "cc"
+	);
+
+	smp_mb();
+
+	return result;
+}
+
+static inline int atomic_sub_if_positive(int i, atomic_t *v)
+{
+	int result, temp;
+
+	__asm__ __volatile__(
+			"1:	LNKGETD %1, [%2]\n"
+			"	SUBS	%1, %1, %3\n"
+			"	LNKSETDGE [%2], %1\n"
+			"	BLT	2f\n"
+			"	DEFR	%0, TXSTAT\n"
+			"	ANDT	%0, %0, #HI(0x3f000000)\n"
+			"	CMPT	%0, #HI(0x02000000)\n"
+			"	BNZ	1b\n"
+			"2:\n"
+			: "=&d" (temp), "=&da" (result)
+			: "da" (&v->counter), "bd" (i)
+			: "cc"
+	);
+
+	return result;
+}
+
+#endif /* __ASM_METAG_ATOMIC_LNKGET_H */
diff --git a/arch/metag/include/asm/atomic_lock1.h b/arch/metag/include/asm/atomic_lock1.h
new file mode 100644
index 0000000..0ce2a9d
--- /dev/null
+++ b/arch/metag/include/asm/atomic_lock1.h
@@ -0,0 +1,160 @@
+#ifndef __ASM_METAG_ATOMIC_LOCK1_H
+#define __ASM_METAG_ATOMIC_LOCK1_H
+
+#define ATOMIC_INIT(i)	{ (i) }
+
+#include <linux/compiler.h>
+
+#include <asm/barrier.h>
+#include <asm/lock.h>
+
+static inline int atomic_read(const atomic_t *v)
+{
+	return (v)->counter;
+}
+
+/*
+ * atomic_set needs to be take the lock to protect atomic_add_unless from a
+ * possible race, as it reads the counter twice:
+ *
+ *  CPU0                               CPU1
+ *  atomic_add_unless(1, 0)
+ *    ret = v->counter (non-zero)
+ *    if (ret != u)                    v->counter = 0
+ *      v->counter += 1 (counter set to 1)
+ *
+ * Making atomic_set take the lock ensures that ordering and logical
+ * consistency is preserved.
+ */
+static inline int atomic_set(atomic_t *v, int i)
+{
+	unsigned long flags;
+
+	__global_lock1(flags);
+	fence();
+	v->counter = i;
+	__global_unlock1(flags);
+	return i;
+}
+
+static inline void atomic_add(int i, atomic_t *v)
+{
+	unsigned long flags;
+
+	__global_lock1(flags);
+	fence();
+	v->counter += i;
+	__global_unlock1(flags);
+}
+
+static inline void atomic_sub(int i, atomic_t *v)
+{
+	unsigned long flags;
+
+	__global_lock1(flags);
+	fence();
+	v->counter -= i;
+	__global_unlock1(flags);
+}
+
+static inline int atomic_add_return(int i, atomic_t *v)
+{
+	unsigned long result;
+	unsigned long flags;
+
+	__global_lock1(flags);
+	result = v->counter;
+	result += i;
+	fence();
+	v->counter = result;
+	__global_unlock1(flags);
+
+	return result;
+}
+
+static inline int atomic_sub_return(int i, atomic_t *v)
+{
+	unsigned long result;
+	unsigned long flags;
+
+	__global_lock1(flags);
+	result = v->counter;
+	result -= i;
+	fence();
+	v->counter = result;
+	__global_unlock1(flags);
+
+	return result;
+}
+
+static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
+{
+	unsigned long flags;
+
+	__global_lock1(flags);
+	fence();
+	v->counter &= ~mask;
+	__global_unlock1(flags);
+}
+
+static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
+{
+	unsigned long flags;
+
+	__global_lock1(flags);
+	fence();
+	v->counter |= mask;
+	__global_unlock1(flags);
+}
+
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+	int ret;
+	unsigned long flags;
+
+	__global_lock1(flags);
+	ret = v->counter;
+	if (ret == old) {
+		fence();
+		v->counter = new;
+	}
+	__global_unlock1(flags);
+
+	return ret;
+}
+
+#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
+
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
+{
+	int ret;
+	unsigned long flags;
+
+	__global_lock1(flags);
+	ret = v->counter;
+	if (ret != u) {
+		fence();
+		v->counter += a;
+	}
+	__global_unlock1(flags);
+
+	return ret;
+}
+
+static inline int atomic_sub_if_positive(int i, atomic_t *v)
+{
+	int ret;
+	unsigned long flags;
+
+	__global_lock1(flags);
+	ret = v->counter - 1;
+	if (ret >= 0) {
+		fence();
+		v->counter = ret;
+	}
+	__global_unlock1(flags);
+
+	return ret;
+}
+
+#endif /* __ASM_METAG_ATOMIC_LOCK1_H */
diff --git a/arch/metag/include/asm/bitops.h b/arch/metag/include/asm/bitops.h
new file mode 100644
index 0000000..ea3ff8a
--- /dev/null
+++ b/arch/metag/include/asm/bitops.h
@@ -0,0 +1,135 @@
+#ifndef __ASM_METAG_BITOPS_H
+#define __ASM_METAG_BITOPS_H
+
+#ifdef __KERNEL__
+#include <linux/compiler.h>
+#include <asm/barrier.h>
+#include <asm/lock.h>
+
+/*
+ * clear_bit() doesn't provide any barrier for the compiler.
+ */
+#define smp_mb__before_clear_bit()	barrier()
+#define smp_mb__after_clear_bit()	barrier()
+
+#ifdef CONFIG_SMP
+/*
+ * These functions are the basis of our bit ops.
+ */
+static inline void set_bit(unsigned int bit, volatile unsigned long *p)
+{
+	unsigned long flags;
+	unsigned long mask = 1UL << (bit & 31);
+
+	p += bit >> 5;
+
+	__global_lock1(flags);
+	fence();
+	*p |= mask;
+	__global_unlock1(flags);
+}
+
+static inline void clear_bit(unsigned int bit, volatile unsigned long *p)
+{
+	unsigned long flags;
+	unsigned long mask = 1UL << (bit & 31);
+
+	p += bit >> 5;
+
+	__global_lock1(flags);
+	fence();
+	*p &= ~mask;
+	__global_unlock1(flags);
+}
+
+static inline void change_bit(unsigned int bit, volatile unsigned long *p)
+{
+	unsigned long flags;
+	unsigned long mask = 1UL << (bit & 31);
+
+	p += bit >> 5;
+
+	__global_lock1(flags);
+	fence();
+	*p ^= mask;
+	__global_unlock1(flags);
+}
+
+static inline int test_and_set_bit(unsigned int bit, volatile unsigned long *p)
+{
+	unsigned long flags;
+	unsigned long old;
+	unsigned long mask = 1UL << (bit & 31);
+
+	p += bit >> 5;
+
+	__global_lock1(flags);
+	old = *p;
+	if (!(old & mask)) {
+		fence();
+		*p = old | mask;
+	}
+	__global_unlock1(flags);
+
+	return (old & mask) != 0;
+}
+
+static inline int test_and_clear_bit(unsigned int bit,
+				     volatile unsigned long *p)
+{
+	unsigned long flags;
+	unsigned long old;
+	unsigned long mask = 1UL << (bit & 31);
+
+	p += bit >> 5;
+
+	__global_lock1(flags);
+	old = *p;
+	if (old & mask) {
+		fence();
+		*p = old & ~mask;
+	}
+	__global_unlock1(flags);
+
+	return (old & mask) != 0;
+}
+
+static inline int test_and_change_bit(unsigned int bit,
+				      volatile unsigned long *p)
+{
+	unsigned long flags;
+	unsigned long old;
+	unsigned long mask = 1UL << (bit & 31);
+
+	p += bit >> 5;
+
+	__global_lock1(flags);
+	fence();
+	old = *p;
+	*p = old ^ mask;
+	__global_unlock1(flags);
+
+	return (old & mask) != 0;
+}
+
+#else
+#include <asm-generic/bitops/atomic.h>
+#endif /* CONFIG_SMP */
+
+#include <asm-generic/bitops/non-atomic.h>
+#include <asm-generic/bitops/find.h>
+#include <asm-generic/bitops/ffs.h>
+#include <asm-generic/bitops/__ffs.h>
+#include <asm-generic/bitops/ffz.h>
+#include <asm-generic/bitops/fls.h>
+#include <asm-generic/bitops/__fls.h>
+#include <asm-generic/bitops/fls64.h>
+#include <asm-generic/bitops/hweight.h>
+#include <asm-generic/bitops/lock.h>
+#include <asm-generic/bitops/sched.h>
+#include <asm-generic/bitops/le.h>
+#include <asm-generic/bitops/ext2-atomic.h>
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_METAG_BITOPS_H */
diff --git a/arch/metag/include/asm/cmpxchg.h b/arch/metag/include/asm/cmpxchg.h
new file mode 100644
index 0000000..d9fa415
--- /dev/null
+++ b/arch/metag/include/asm/cmpxchg.h
@@ -0,0 +1,65 @@
+#ifndef __ASM_METAG_CMPXCHG_H
+#define __ASM_METAG_CMPXCHG_H
+
+#include <asm/barrier.h>
+
+#if defined(CONFIG_ATOMICITY_IRQSOFF)
+#include <asm/cmpxchg_irq.h>
+#elif defined(CONFIG_ATOMICITY_LOCK1)
+#include <asm/cmpxchg_lock1.h>
+#elif defined(CONFIG_ATOMICITY_LNKGET)
+#include <asm/cmpxchg_lnkget.h>
+#endif
+
+extern void __xchg_called_with_bad_pointer(void);
+
+#define __xchg(ptr, x, size)				\
+({							\
+	unsigned long __xchg__res;			\
+	volatile void *__xchg_ptr = (ptr);		\
+	switch (size) {					\
+	case 4:						\
+		__xchg__res = xchg_u32(__xchg_ptr, x);	\
+		break;					\
+	case 1:						\
+		__xchg__res = xchg_u8(__xchg_ptr, x);	\
+		break;					\
+	default:					\
+		__xchg_called_with_bad_pointer();	\
+		__xchg__res = x;			\
+		break;					\
+	}						\
+							\
+	__xchg__res;					\
+})
+
+#define xchg(ptr, x)	\
+	((__typeof__(*(ptr)))__xchg((ptr), (unsigned long)(x), sizeof(*(ptr))))
+
+/* This function doesn't exist, so you'll get a linker error
+ * if something tries to do an invalid cmpxchg(). */
+extern void __cmpxchg_called_with_bad_pointer(void);
+
+static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
+				      unsigned long new, int size)
+{
+	switch (size) {
+	case 4:
+		return __cmpxchg_u32(ptr, old, new);
+	}
+	__cmpxchg_called_with_bad_pointer();
+	return old;
+}
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+#define cmpxchg(ptr, o, n)						\
+	({								\
+		__typeof__(*(ptr)) _o_ = (o);				\
+		__typeof__(*(ptr)) _n_ = (n);				\
+		(__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
+					       (unsigned long)_n_,	\
+					       sizeof(*(ptr)));		\
+	})
+
+#endif /* __ASM_METAG_CMPXCHG_H */
diff --git a/arch/metag/include/asm/cmpxchg_irq.h b/arch/metag/include/asm/cmpxchg_irq.h
new file mode 100644
index 0000000..6495731
--- /dev/null
+++ b/arch/metag/include/asm/cmpxchg_irq.h
@@ -0,0 +1,42 @@
+#ifndef __ASM_METAG_CMPXCHG_IRQ_H
+#define __ASM_METAG_CMPXCHG_IRQ_H
+
+#include <linux/irqflags.h>
+
+static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val)
+{
+	unsigned long flags, retval;
+
+	local_irq_save(flags);
+	retval = *m;
+	*m = val;
+	local_irq_restore(flags);
+	return retval;
+}
+
+static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val)
+{
+	unsigned long flags, retval;
+
+	local_irq_save(flags);
+	retval = *m;
+	*m = val & 0xff;
+	local_irq_restore(flags);
+	return retval;
+}
+
+static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old,
+					  unsigned long new)
+{
+	__u32 retval;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	retval = *m;
+	if (retval == old)
+		*m = new;
+	local_irq_restore(flags);       /* implies memory barrier  */
+	return retval;
+}
+
+#endif /* __ASM_METAG_CMPXCHG_IRQ_H */
diff --git a/arch/metag/include/asm/cmpxchg_lnkget.h b/arch/metag/include/asm/cmpxchg_lnkget.h
new file mode 100644
index 0000000..9800838
--- /dev/null
+++ b/arch/metag/include/asm/cmpxchg_lnkget.h
@@ -0,0 +1,86 @@
+#ifndef __ASM_METAG_CMPXCHG_LNKGET_H
+#define __ASM_METAG_CMPXCHG_LNKGET_H
+
+static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val)
+{
+	int temp, old;
+
+	smp_mb();
+
+	__asm__ __volatile__(
+			"1:	LNKGETD %1, [%2]\n"
+			"	LNKSETD	[%2], %3\n"
+			"	DEFR	%0, TXSTAT\n"
+			"	ANDT	%0, %0, #HI(0x3f000000)\n"
+			"	CMPT	%0, #HI(0x02000000)\n"
+			"	BNZ	1b\n"
+#ifdef CONFIG_LNKGET_AROUND_CACHE
+			"	DCACHE	[%2], %0\n"
+#endif
+			: "=&d" (temp), "=&d" (old)
+			: "da" (m), "da" (val)
+			: "cc"
+	);
+
+	smp_mb();
+
+	return old;
+}
+
+static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val)
+{
+	int temp, old;
+
+	smp_mb();
+
+	__asm__ __volatile__(
+			"1:	LNKGETD %1, [%2]\n"
+			"	LNKSETD	[%2], %3\n"
+			"	DEFR	%0, TXSTAT\n"
+			"	ANDT	%0, %0, #HI(0x3f000000)\n"
+			"	CMPT	%0, #HI(0x02000000)\n"
+			"	BNZ	1b\n"
+#ifdef CONFIG_LNKGET_AROUND_CACHE
+			"	DCACHE	[%2], %0\n"
+#endif
+			: "=&d" (temp), "=&d" (old)
+			: "da" (m), "da" (val & 0xff)
+			: "cc"
+	);
+
+	smp_mb();
+
+	return old;
+}
+
+static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old,
+					  unsigned long new)
+{
+	__u32 retval, temp;
+
+	smp_mb();
+
+	__asm__ __volatile__(
+		"1:	LNKGETD	%1, [%2]\n"
+		"	CMP	%1, %3\n"
+		"	LNKSETDEQ [%2], %4\n"
+		"	BNE	2f\n"
+		"	DEFR	%0, TXSTAT\n"
+		"	ANDT	%0, %0, #HI(0x3f000000)\n"
+		"	CMPT	%0, #HI(0x02000000)\n"
+		"	BNZ	1b\n"
+#ifdef CONFIG_LNKGET_AROUND_CACHE
+		"	DCACHE	[%2], %0\n"
+#endif
+		"2:\n"
+		: "=&d" (temp), "=&da" (retval)
+		: "da" (m), "bd" (old), "da" (new)
+		: "cc"
+	);
+
+	smp_mb();
+
+	return retval;
+}
+
+#endif /* __ASM_METAG_CMPXCHG_LNKGET_H */
diff --git a/arch/metag/include/asm/cmpxchg_lock1.h b/arch/metag/include/asm/cmpxchg_lock1.h
new file mode 100644
index 0000000..7fb4e1c
--- /dev/null
+++ b/arch/metag/include/asm/cmpxchg_lock1.h
@@ -0,0 +1,48 @@
+#ifndef __ASM_METAG_CMPXCHG_LOCK1_H
+#define __ASM_METAG_CMPXCHG_LOCK1_H
+
+#include <asm/lock.h>
+
+/* Use LOCK2 as these have to be atomic w.r.t. ordinary accesses. */
+
+static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val)
+{
+	unsigned long flags, retval;
+
+	__global_lock2(flags);
+	fence();
+	retval = *m;
+	*m = val;
+	__global_unlock2(flags);
+	return retval;
+}
+
+static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val)
+{
+	unsigned long flags, retval;
+
+	__global_lock2(flags);
+	fence();
+	retval = *m;
+	*m = val & 0xff;
+	__global_unlock2(flags);
+	return retval;
+}
+
+static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old,
+					  unsigned long new)
+{
+	__u32 retval;
+	unsigned long flags;
+
+	__global_lock2(flags);
+	retval = *m;
+	if (retval == old) {
+		fence();
+		*m = new;
+	}
+	__global_unlock2(flags);
+	return retval;
+}
+
+#endif /* __ASM_METAG_CMPXCHG_LOCK1_H */
diff --git a/arch/metag/include/asm/lock.h b/arch/metag/include/asm/lock.h
new file mode 100644
index 0000000..f419839
--- /dev/null
+++ b/arch/metag/include/asm/lock.h
@@ -0,0 +1,17 @@
+#ifndef __ASM_METAG_LOCK_H
+#define __ASM_METAG_LOCK_H
+
+#define METAG_ALL_VALUES
+#define METAC_ALL_VALUES
+#include <asm/tbx/machine.inc>
+#include <asm/tbx/metagtbx.h>
+
+#include <linux/compiler.h>
+
+#define __global_lock1(flags) { TBI_CRITON(flags); barrier(); }
+#define __global_unlock1(flags) { barrier(); TBI_CRITOFF(flags); }
+
+#define __global_lock2(flags) { TBI_LOCK(flags); barrier(); }
+#define __global_unlock2(flags) { barrier(); TBI_UNLOCK(flags); }
+
+#endif /* __ASM_METAG_LOCK_H */
diff --git a/arch/metag/include/asm/spinlock.h b/arch/metag/include/asm/spinlock.h
new file mode 100644
index 0000000..23dad8c
--- /dev/null
+++ b/arch/metag/include/asm/spinlock.h
@@ -0,0 +1,22 @@
+#ifndef __ASM_SPINLOCK_H
+#define __ASM_SPINLOCK_H
+
+#ifdef CONFIG_ATOMICITY_LOCK1
+#include <asm/spinlock_lock1.h>
+#else
+#include <asm/spinlock_lnkget.h>
+#endif
+
+#define arch_spin_unlock_wait(lock) \
+	do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0)
+
+#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
+
+#define	arch_read_lock_flags(lock, flags) arch_read_lock(lock)
+#define	arch_write_lock_flags(lock, flags) arch_write_lock(lock)
+
+#define arch_spin_relax(lock)	cpu_relax()
+#define arch_read_relax(lock)	cpu_relax()
+#define arch_write_relax(lock)	cpu_relax()
+
+#endif /* __ASM_SPINLOCK_H */
diff --git a/arch/metag/include/asm/spinlock_lnkget.h b/arch/metag/include/asm/spinlock_lnkget.h
new file mode 100644
index 0000000..0990e39
--- /dev/null
+++ b/arch/metag/include/asm/spinlock_lnkget.h
@@ -0,0 +1,261 @@
+#ifndef __ASM_SPINLOCK_LNKGET_H
+#define __ASM_SPINLOCK_LNKGET_H
+
+/*
+ * None of these asm statements clobber memory as LNKSET writes around
+ * the cache so the memory it modifies cannot safely be read by any means
+ * other than these accessors.
+ */
+
+static inline int arch_spin_is_locked(arch_spinlock_t *lock)
+{
+	int ret;
+
+	__asm__ __volatile__(
+			     "LNKGETD	%0, [%1]\n"
+			     "TST	%0, #1\n"
+			     "MOV	%0, #1\n"
+			     "XORZ      %0, %0, %0\n"
+			     : "=&d" (ret)
+			     : "da" (&lock->lock)
+			     : "cc");
+	return ret;
+}
+
+static inline void arch_spin_lock(arch_spinlock_t *lock)
+{
+	int tmp;
+
+	__asm__ __volatile__(
+			     "1:     LNKGETD %0,[%1]\n"
+			     "       TST     %0, #1\n"
+			     "       ADD     %0, %0, #1\n"
+			     "       LNKSETDZ [%1], %0\n"
+			     "       BNZ     1b\n"
+			     "       DEFR    %0, TXSTAT\n"
+			     "       ANDT    %0, %0, #HI(0x3f000000)\n"
+			     "       CMPT    %0, #HI(0x02000000)\n"
+			     "       BNZ     1b\n"
+			     : "=&d" (tmp)
+			     : "da" (&lock->lock)
+			     : "cc");
+
+	smp_mb();
+}
+
+/* Returns 0 if failed to acquire lock */
+static inline int arch_spin_trylock(arch_spinlock_t *lock)
+{
+	int tmp;
+
+	__asm__ __volatile__(
+			     "       LNKGETD %0,[%1]\n"
+			     "       TST     %0, #1\n"
+			     "       ADD     %0, %0, #1\n"
+			     "       LNKSETDZ [%1], %0\n"
+			     "       BNZ     1f\n"
+			     "       DEFR    %0, TXSTAT\n"
+			     "       ANDT    %0, %0, #HI(0x3f000000)\n"
+			     "       CMPT    %0, #HI(0x02000000)\n"
+			     "       MOV     %0, #1\n"
+			     "1:     XORNZ   %0, %0, %0\n"
+			     : "=&d" (tmp)
+			     : "da" (&lock->lock)
+			     : "cc");
+
+	smp_mb();
+
+	return tmp;
+}
+
+static inline void arch_spin_unlock(arch_spinlock_t *lock)
+{
+	smp_mb();
+
+	__asm__ __volatile__(
+			     "       SETD    [%0], %1\n"
+			     :
+			     : "da" (&lock->lock), "da" (0)
+			     : "memory");
+}
+
+/*
+ * RWLOCKS
+ *
+ *
+ * Write locks are easy - we just set bit 31.  When unlocking, we can
+ * just write zero since the lock is exclusively held.
+ */
+
+static inline void arch_write_lock(arch_rwlock_t *rw)
+{
+	int tmp;
+
+	__asm__ __volatile__(
+			     "1:     LNKGETD %0,[%1]\n"
+			     "       CMP     %0, #0\n"
+			     "       ADD     %0, %0, %2\n"
+			     "       LNKSETDZ [%1], %0\n"
+			     "       BNZ     1b\n"
+			     "       DEFR    %0, TXSTAT\n"
+			     "       ANDT    %0, %0, #HI(0x3f000000)\n"
+			     "       CMPT    %0, #HI(0x02000000)\n"
+			     "       BNZ     1b\n"
+			     : "=&d" (tmp)
+			     : "da" (&rw->lock), "bd" (0x80000000)
+			     : "cc");
+
+	smp_mb();
+}
+
+static inline int arch_write_trylock(arch_rwlock_t *rw)
+{
+	int tmp;
+
+	__asm__ __volatile__(
+			     "       LNKGETD %0,[%1]\n"
+			     "       CMP     %0, #0\n"
+			     "       ADD     %0, %0, %2\n"
+			     "       LNKSETDZ [%1], %0\n"
+			     "       BNZ     1f\n"
+			     "       DEFR    %0, TXSTAT\n"
+			     "       ANDT    %0, %0, #HI(0x3f000000)\n"
+			     "       CMPT    %0, #HI(0x02000000)\n"
+			     "       MOV     %0,#1\n"
+			     "1:     XORNZ   %0, %0, %0\n"
+			     : "=&d" (tmp)
+			     : "da" (&rw->lock), "bd" (0x80000000)
+			     : "cc");
+
+	smp_mb();
+
+	return tmp;
+}
+
+static inline void arch_write_unlock(arch_rwlock_t *rw)
+{
+	smp_mb();
+
+	__asm__ __volatile__(
+			     "       SETD    [%0], %1\n"
+			     :
+			     : "da" (&rw->lock), "da" (0)
+			     : "memory");
+}
+
+/* write_can_lock - would write_trylock() succeed? */
+static inline int arch_write_can_lock(arch_rwlock_t *rw)
+{
+	int ret;
+
+	__asm__ __volatile__(
+			     "LNKGETD	%0, [%1]\n"
+			     "CMP	%0, #0\n"
+			     "MOV	%0, #1\n"
+			     "XORNZ     %0, %0, %0\n"
+			     : "=&d" (ret)
+			     : "da" (&rw->lock)
+			     : "cc");
+	return ret;
+}
+
+/*
+ * Read locks are a bit more hairy:
+ *  - Exclusively load the lock value.
+ *  - Increment it.
+ *  - Store new lock value if positive, and we still own this location.
+ *    If the value is negative, we've already failed.
+ *  - If we failed to store the value, we want a negative result.
+ *  - If we failed, try again.
+ * Unlocking is similarly hairy.  We may have multiple read locks
+ * currently active.  However, we know we won't have any write
+ * locks.
+ */
+static inline void arch_read_lock(arch_rwlock_t *rw)
+{
+	int tmp;
+
+	__asm__ __volatile__(
+			     "1:     LNKGETD %0,[%1]\n"
+			     "       ADDS    %0, %0, #1\n"
+			     "       LNKSETDPL [%1], %0\n"
+			     "       BMI     1b\n"
+			     "       DEFR    %0, TXSTAT\n"
+			     "       ANDT    %0, %0, #HI(0x3f000000)\n"
+			     "       CMPT    %0, #HI(0x02000000)\n"
+			     "       BNZ     1b\n"
+			     : "=&d" (tmp)
+			     : "da" (&rw->lock)
+			     : "cc");
+
+	smp_mb();
+}
+
+static inline void arch_read_unlock(arch_rwlock_t *rw)
+{
+	int tmp;
+
+	smp_mb();
+
+	__asm__ __volatile__(
+			     "1:     LNKGETD %0,[%1]\n"
+			     "       SUB     %0, %0, #1\n"
+			     "       LNKSETD [%1], %0\n"
+			     "       DEFR    %0, TXSTAT\n"
+			     "       ANDT    %0, %0, #HI(0x3f000000)\n"
+			     "       CMPT    %0, #HI(0x02000000)\n"
+			     "       BNZ     1b\n"
+			     : "=&d" (tmp)
+			     : "da" (&rw->lock)
+			     : "cc", "memory");
+}
+
+static inline int arch_read_trylock(arch_rwlock_t *rw)
+{
+	int tmp;
+
+	__asm__ __volatile__(
+			     "       LNKGETD %0,[%1]\n"
+			     "       ADDS    %0, %0, #1\n"
+			     "       LNKSETDPL [%1], %0\n"
+			     "       BMI     1f\n"
+			     "       DEFR    %0, TXSTAT\n"
+			     "       ANDT    %0, %0, #HI(0x3f000000)\n"
+			     "       CMPT    %0, #HI(0x02000000)\n"
+			     "       MOV     %0,#1\n"
+			     "       BZ      2f\n"
+			     "1:     MOV     %0,#0\n"
+			     "2:\n"
+			     : "=&d" (tmp)
+			     : "da" (&rw->lock)
+			     : "cc");
+
+	smp_mb();
+
+	return tmp;
+}
+
+/* read_can_lock - would read_trylock() succeed? */
+static inline int arch_read_can_lock(arch_rwlock_t *rw)
+{
+	int tmp;
+
+	__asm__ __volatile__(
+			     "LNKGETD	%0, [%1]\n"
+			     "CMP	%0, %2\n"
+			     "MOV	%0, #1\n"
+			     "XORZ	%0, %0, %0\n"
+			     : "=&d" (tmp)
+			     : "da" (&rw->lock), "bd" (0x80000000)
+			     : "cc");
+	return tmp;
+}
+
+#define	arch_read_lock_flags(lock, flags) arch_read_lock(lock)
+#define	arch_write_lock_flags(lock, flags) arch_write_lock(lock)
+
+#define arch_spin_relax(lock)	cpu_relax()
+#define arch_read_relax(lock)	cpu_relax()
+#define arch_write_relax(lock)	cpu_relax()
+
+#endif /* __ASM_SPINLOCK_LNKGET_H */
diff --git a/arch/metag/include/asm/spinlock_lock1.h b/arch/metag/include/asm/spinlock_lock1.h
new file mode 100644
index 0000000..e9f27df
--- /dev/null
+++ b/arch/metag/include/asm/spinlock_lock1.h
@@ -0,0 +1,184 @@
+#ifndef __ASM_SPINLOCK_LOCK1_H
+#define __ASM_SPINLOCK_LOCK1_H
+
+#include <asm/bug.h>
+#include <asm/lock.h>
+
+static inline int arch_spin_is_locked(arch_spinlock_t *lock)
+{
+	int ret;
+
+	barrier();
+	ret = lock->lock;
+	WARN_ON(ret != 0 && ret != 1);
+	return ret;
+}
+
+static inline void arch_spin_lock(arch_spinlock_t *lock)
+{
+	unsigned int we_won = 0;
+	unsigned long flags;
+
+again:
+	__global_lock1(flags);
+	if (lock->lock == 0) {
+		fence();
+		lock->lock = 1;
+		we_won = 1;
+	}
+	__global_unlock1(flags);
+	if (we_won == 0)
+		goto again;
+	WARN_ON(lock->lock != 1);
+}
+
+/* Returns 0 if failed to acquire lock */
+static inline int arch_spin_trylock(arch_spinlock_t *lock)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	__global_lock1(flags);
+	ret = lock->lock;
+	if (ret == 0) {
+		fence();
+		lock->lock = 1;
+	}
+	__global_unlock1(flags);
+	return (ret == 0);
+}
+
+static inline void arch_spin_unlock(arch_spinlock_t *lock)
+{
+	barrier();
+	WARN_ON(!lock->lock);
+	lock->lock = 0;
+}
+
+/*
+ * RWLOCKS
+ *
+ *
+ * Write locks are easy - we just set bit 31.  When unlocking, we can
+ * just write zero since the lock is exclusively held.
+ */
+
+static inline void arch_write_lock(arch_rwlock_t *rw)
+{
+	unsigned long flags;
+	unsigned int we_won = 0;
+
+again:
+	__global_lock1(flags);
+	if (rw->lock == 0) {
+		fence();
+		rw->lock = 0x80000000;
+		we_won = 1;
+	}
+	__global_unlock1(flags);
+	if (we_won == 0)
+		goto again;
+	WARN_ON(rw->lock != 0x80000000);
+}
+
+static inline int arch_write_trylock(arch_rwlock_t *rw)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	__global_lock1(flags);
+	ret = rw->lock;
+	if (ret == 0) {
+		fence();
+		rw->lock = 0x80000000;
+	}
+	__global_unlock1(flags);
+
+	return (ret == 0);
+}
+
+static inline void arch_write_unlock(arch_rwlock_t *rw)
+{
+	barrier();
+	WARN_ON(rw->lock != 0x80000000);
+	rw->lock = 0;
+}
+
+/* write_can_lock - would write_trylock() succeed? */
+static inline int arch_write_can_lock(arch_rwlock_t *rw)
+{
+	unsigned int ret;
+
+	barrier();
+	ret = rw->lock;
+	return (ret == 0);
+}
+
+/*
+ * Read locks are a bit more hairy:
+ *  - Exclusively load the lock value.
+ *  - Increment it.
+ *  - Store new lock value if positive, and we still own this location.
+ *    If the value is negative, we've already failed.
+ *  - If we failed to store the value, we want a negative result.
+ *  - If we failed, try again.
+ * Unlocking is similarly hairy.  We may have multiple read locks
+ * currently active.  However, we know we won't have any write
+ * locks.
+ */
+static inline void arch_read_lock(arch_rwlock_t *rw)
+{
+	unsigned long flags;
+	unsigned int we_won = 0, ret;
+
+again:
+	__global_lock1(flags);
+	ret = rw->lock;
+	if (ret < 0x80000000) {
+		fence();
+		rw->lock = ret + 1;
+		we_won = 1;
+	}
+	__global_unlock1(flags);
+	if (!we_won)
+		goto again;
+}
+
+static inline void arch_read_unlock(arch_rwlock_t *rw)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	__global_lock1(flags);
+	fence();
+	ret = rw->lock--;
+	__global_unlock1(flags);
+	WARN_ON(ret == 0);
+}
+
+static inline int arch_read_trylock(arch_rwlock_t *rw)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	__global_lock1(flags);
+	ret = rw->lock;
+	if (ret < 0x80000000) {
+		fence();
+		rw->lock = ret + 1;
+	}
+	__global_unlock1(flags);
+	return (ret < 0x80000000);
+}
+
+/* read_can_lock - would read_trylock() succeed? */
+static inline int arch_read_can_lock(arch_rwlock_t *rw)
+{
+	unsigned int ret;
+
+	barrier();
+	ret = rw->lock;
+	return (ret < 0x80000000);
+}
+
+#endif /* __ASM_SPINLOCK_LOCK1_H */
diff --git a/arch/metag/include/asm/spinlock_types.h b/arch/metag/include/asm/spinlock_types.h
new file mode 100644
index 0000000..b763914
--- /dev/null
+++ b/arch/metag/include/asm/spinlock_types.h
@@ -0,0 +1,20 @@
+#ifndef _ASM_METAG_SPINLOCK_TYPES_H
+#define _ASM_METAG_SPINLOCK_TYPES_H
+
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
+typedef struct {
+	volatile unsigned int lock;
+} arch_spinlock_t;
+
+#define __ARCH_SPIN_LOCK_UNLOCKED	{ 0 }
+
+typedef struct {
+	volatile unsigned int lock;
+} arch_rwlock_t;
+
+#define __ARCH_RW_LOCK_UNLOCKED		{ 0 }
+
+#endif /* _ASM_METAG_SPINLOCK_TYPES_H */
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 22/40] metag: Basic documentation
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (18 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 21/40] metag: Atomics, locks and bitops James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 23/40] metag: SMP support James Hogan
                   ` (20 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add basic metag documentation.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 Documentation/00-INDEX              |    2 ++
 Documentation/kernel-parameters.txt |    4 ++++
 Documentation/metag/00-INDEX        |    2 ++
 3 files changed, 8 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/metag/00-INDEX

diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
index 49c0513..1dadc7e 100644
--- a/Documentation/00-INDEX
+++ b/Documentation/00-INDEX
@@ -226,6 +226,8 @@ memory-hotplug.txt
 	- Hotpluggable memory support, how to use and current status.
 memory.txt
 	- info on typical Linux memory problems.
+metag/
+	- directory with info about Linux on Meta architecture.
 mips/
 	- directory with info about Linux on MIPS architecture.
 mmc/
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index ad7e2e5..4989844 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -958,6 +958,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			       If specified, z/VM IUCV HVC accepts connections
 			       from listed z/VM user IDs only.
 
+	hwthread_map=	[METAG] Comma-separated list of Linux cpu id to
+			        hardware thread id mappings.
+				Format: <cpu>:<hwthread>
+
 	keep_bootcon	[KNL]
 			Do not unregister boot console at start. This is only
 			useful for debugging when something happens in the window
diff --git a/Documentation/metag/00-INDEX b/Documentation/metag/00-INDEX
new file mode 100644
index 0000000..808f5d4
--- /dev/null
+++ b/Documentation/metag/00-INDEX
@@ -0,0 +1,2 @@
+00-INDEX
+	- this file
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 23/40] metag: SMP support
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (19 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 22/40] metag: Basic documentation James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 24/40] metag: DMA James Hogan
                   ` (19 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add SMP support for metag. This allows Linux to take control of multiple
hardware threads on a single Meta core, treating them as separate Linux
CPUs.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/cachepart.h |   42 +++
 arch/metag/include/asm/core_reg.h  |    7 +
 arch/metag/include/asm/smp.h       |   31 ++
 arch/metag/include/asm/topology.h  |   53 ++++
 arch/metag/kernel/cachepart.c      |  125 ++++++++
 arch/metag/kernel/core_reg.c       |  120 ++++++++
 arch/metag/kernel/head.S           |   12 +
 arch/metag/kernel/smp.c            |  582 ++++++++++++++++++++++++++++++++++++
 arch/metag/kernel/topology.c       |   77 +++++
 9 files changed, 1049 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/cachepart.h
 create mode 100644 arch/metag/include/asm/core_reg.h
 create mode 100644 arch/metag/include/asm/smp.h
 create mode 100644 arch/metag/include/asm/topology.h
 create mode 100644 arch/metag/kernel/cachepart.c
 create mode 100644 arch/metag/kernel/core_reg.c
 create mode 100644 arch/metag/kernel/smp.c
 create mode 100644 arch/metag/kernel/topology.c

diff --git a/arch/metag/include/asm/cachepart.h b/arch/metag/include/asm/cachepart.h
new file mode 100644
index 0000000..cf6b44e
--- /dev/null
+++ b/arch/metag/include/asm/cachepart.h
@@ -0,0 +1,42 @@
+/*
+ * Meta cache partition manipulation.
+ *
+ * Copyright 2010 Imagination Technologies Ltd.
+ */
+
+#ifndef _METAG_CACHEPART_H_
+#define _METAG_CACHEPART_H_
+
+/**
+ * get_dcache_size() - Get size of data cache.
+ */
+unsigned int get_dcache_size(void);
+
+/**
+ * get_icache_size() - Get size of code cache.
+ */
+unsigned int get_icache_size(void);
+
+/**
+ * get_global_dcache_size() - Get the thread's global dcache.
+ *
+ * Returns the size of the current thread's global dcache partition.
+ */
+unsigned int get_global_dcache_size(void);
+
+/**
+ * get_global_icache_size() - Get the thread's global icache.
+ *
+ * Returns the size of the current thread's global icache partition.
+ */
+unsigned int get_global_icache_size(void);
+
+/**
+ * check_for_dache_aliasing() - Ensure that the bootloader has configured the
+ * dache and icache properly to avoid aliasing
+ * @thread_id: Hardware thread ID
+ *
+ */
+void check_for_cache_aliasing(int thread_id);
+
+#endif
diff --git a/arch/metag/include/asm/core_reg.h b/arch/metag/include/asm/core_reg.h
new file mode 100644
index 0000000..62a4a3a
--- /dev/null
+++ b/arch/metag/include/asm/core_reg.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_METAG_CORE_REG_H_
+#define __ASM_METAG_CORE_REG_H_
+
+extern void core_reg_write(int unit, int reg, int thread, unsigned int val);
+extern unsigned int core_reg_read(int unit, int reg, int thread);
+
+#endif
diff --git a/arch/metag/include/asm/smp.h b/arch/metag/include/asm/smp.h
new file mode 100644
index 0000000..b35fa84
--- /dev/null
+++ b/arch/metag/include/asm/smp.h
@@ -0,0 +1,31 @@
+#ifndef __ASM_SMP_H
+#define __ASM_SMP_H
+
+#include <linux/cpumask.h>
+
+#define raw_smp_processor_id() (current_thread_info()->cpu)
+
+extern void local_timer_setup(unsigned int cpu);
+
+enum ipi_msg_type {
+	IPI_CALL_FUNC,
+	IPI_CALL_FUNC_SINGLE,
+	IPI_RESCHEDULE,
+};
+
+extern void arch_send_call_function_single_ipi(int cpu);
+extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
+#define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask
+
+asmlinkage void secondary_start_kernel(void);
+
+extern void secondary_startup(void);
+
+#ifdef CONFIG_HOTPLUG_CPU
+extern void __cpu_die(unsigned int cpu);
+extern int __cpu_disable(void);
+extern void cpu_die(void);
+#endif
+
+extern void smp_init_cpus(void);
+#endif /* __ASM_SMP_H */
diff --git a/arch/metag/include/asm/topology.h b/arch/metag/include/asm/topology.h
new file mode 100644
index 0000000..23f5118
--- /dev/null
+++ b/arch/metag/include/asm/topology.h
@@ -0,0 +1,53 @@
+#ifndef _ASM_METAG_TOPOLOGY_H
+#define _ASM_METAG_TOPOLOGY_H
+
+#ifdef CONFIG_NUMA
+
+/* sched_domains SD_NODE_INIT for Meta machines */
+#define SD_NODE_INIT (struct sched_domain) {		\
+	.parent			= NULL,			\
+	.child			= NULL,			\
+	.groups			= NULL,			\
+	.min_interval		= 8,			\
+	.max_interval		= 32,			\
+	.busy_factor		= 32,			\
+	.imbalance_pct		= 125,			\
+	.cache_nice_tries	= 2,			\
+	.busy_idx		= 3,			\
+	.idle_idx		= 2,			\
+	.newidle_idx		= 0,			\
+	.wake_idx		= 0,			\
+	.forkexec_idx		= 0,			\
+	.flags			= SD_LOAD_BALANCE	\
+				| SD_BALANCE_FORK	\
+				| SD_BALANCE_EXEC	\
+				| SD_BALANCE_NEWIDLE	\
+				| SD_SERIALIZE,		\
+	.last_balance		= jiffies,		\
+	.balance_interval	= 1,			\
+	.nr_balance_failed	= 0,			\
+}
+
+#define cpu_to_node(cpu)	((void)(cpu), 0)
+#define parent_node(node)	((void)(node), 0)
+
+#define cpumask_of_node(node)	((void)node, cpu_online_mask)
+
+#define pcibus_to_node(bus)	((void)(bus), -1)
+#define cpumask_of_pcibus(bus)	(pcibus_to_node(bus) == -1 ? \
+					cpu_all_mask : \
+					cpumask_of_node(pcibus_to_node(bus)))
+
+#endif
+
+#define mc_capable()    (1)
+
+const struct cpumask *cpu_coregroup_mask(unsigned int cpu);
+
+extern cpumask_t cpu_core_map[NR_CPUS];
+
+#define topology_core_cpumask(cpu)	(&cpu_core_map[cpu])
+
+#include <asm-generic/topology.h>
+
+#endif /* _ASM_METAG_TOPOLOGY_H */
diff --git a/arch/metag/kernel/cachepart.c b/arch/metag/kernel/cachepart.c
new file mode 100644
index 0000000..0694914
--- /dev/null
+++ b/arch/metag/kernel/cachepart.c
@@ -0,0 +1,125 @@
+/*
+ * Meta cache partition manipulation.
+ *
+ * Copyright 2010 Imagination Technologies Ltd.
+ */
+
+#define METAC_SYSC_VALUES
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <asm/processor.h>
+#include <asm/tbx/machine.inc>
+#include <asm/cachepart.h>
+
+#define SYSC_DCPART(n)	(SYSC_DCPART0 + SYSC_xCPARTn_STRIDE * (n))
+#define SYSC_ICPART(n)	(SYSC_ICPART0 + SYSC_xCPARTn_STRIDE * (n))
+
+#define CACHE_ASSOCIATIVITY 4 /* 4 way set-assosiative */
+#define ICACHE 0
+#define DCACHE 1
+
+/* The CORE_CONFIG2 register is not available on Meta 1 */
+#ifdef CONFIG_META21
+unsigned int get_dcache_size(void)
+{
+	unsigned int config2 = readl(METAC_CORE_CONFIG2);
+	return 0x1000 << ((config2 & METAC_CORECFG2_DCSZ_BITS)
+				>> METAC_CORECFG2_DCSZ_S);
+}
+
+unsigned int get_icache_size(void)
+{
+	unsigned int config2 = readl(METAC_CORE_CONFIG2);
+	return 0x1000 << ((config2 & METAC_CORE_C2ICSZ_BITS)
+				>> METAC_CORE_C2ICSZ_S);
+}
+
+unsigned int get_global_dcache_size(void)
+{
+	unsigned int cpart = readl(SYSC_DCPART(hard_processor_id()));
+	unsigned int temp = cpart & SYSC_xCPARTG_AND_BITS;
+	return (get_dcache_size() * ((temp >> SYSC_xCPARTG_AND_S) + 1)) >> 4;
+}
+
+unsigned int get_global_icache_size(void)
+{
+	unsigned int cpart = readl(SYSC_ICPART(hard_processor_id()));
+	unsigned int temp = cpart & SYSC_xCPARTG_AND_BITS;
+	return (get_icache_size() * ((temp >> SYSC_xCPARTG_AND_S) + 1)) >> 4;
+}
+
+static unsigned int get_thread_cache_size(unsigned int cache, int thread_id)
+{
+	unsigned int cache_size;
+	unsigned int t_cache_part;
+	unsigned int isEnabled;
+	unsigned int offset = 0;
+	isEnabled = (cache == DCACHE ? readl(MMCU_DCACHE_CTRL_ADDR) & 0x1 :
+		readl(MMCU_ICACHE_CTRL_ADDR) & 0x1);
+	if (!isEnabled)
+		return 0;
+#if PAGE_OFFSET >= LINGLOBAL_BASE
+	/* Checking for global cache */
+	cache_size = (cache == DCACHE ? get_global_dache_size() :
+		get_global_icache_size());
+	offset = 8;
+#else
+	cache_size = (cache == DCACHE ? get_dcache_size() :
+		get_icache_size());
+#endif
+	t_cache_part = (cache == DCACHE ?
+		(readl(SYSC_DCPART(thread_id)) >> offset) & 0xF :
+		(readl(SYSC_ICPART(thread_id)) >> offset) & 0xF);
+	switch (t_cache_part) {
+	case 0xF:
+		return cache_size;
+	case 0x7:
+		return cache_size / 2;
+	case 0x3:
+		return cache_size / 4;
+	case 0x1:
+		return cache_size / 8;
+	case 0:
+		return cache_size / 16;
+	}
+	return -1;
+}
+
+void check_for_cache_aliasing(int thread_id)
+{
+	unsigned int thread_cache_size;
+	unsigned int cache_type;
+	for (cache_type = ICACHE; cache_type <= DCACHE; cache_type++) {
+		thread_cache_size =
+				get_thread_cache_size(cache_type, thread_id);
+		if (thread_cache_size < 0)
+			pr_emerg("Can't read %s cache size", \
+				 cache_type ? "DCACHE" : "ICACHE");
+		else if (thread_cache_size == 0)
+			/* Cache is off. No need to check for aliasing */
+			continue;
+		if (thread_cache_size / CACHE_ASSOCIATIVITY > PAGE_SIZE) {
+			pr_emerg("Cache aliasing detected in %s on Thread %d",
+				 cache_type ? "DCACHE" : "ICACHE", thread_id);
+			pr_warn("Total %s size: %u bytes",
+				cache_type ? "DCACHE" : "ICACHE ",
+				cache_type ? get_dcache_size()
+				: get_icache_size());
+			pr_warn("Thread %s size: %d bytes",
+				cache_type ? "CACHE" : "ICACHE",
+				thread_cache_size);
+			pr_warn("Page Size: %lu bytes", PAGE_SIZE);
+		}
+	}
+}
+
+#else
+
+void check_for_cache_aliasing(int thread_id)
+{
+	return;
+}
+
+#endif
diff --git a/arch/metag/kernel/core_reg.c b/arch/metag/kernel/core_reg.c
new file mode 100644
index 0000000..7b961d2
--- /dev/null
+++ b/arch/metag/kernel/core_reg.c
@@ -0,0 +1,120 @@
+/*
+ *  Support for reading and writing Meta core internal registers.
+ *
+ *  Copyright (C) 2011 Imagination Technologies Ltd.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/export.h>
+
+#include <asm/core_reg.h>
+#include <asm/io.h>
+#include <asm/hwthread.h>
+
+#define METAG_ALL_VALUES
+#define METAC_ALL_VALUES
+#define METAG_TXUXXRX_VALUES
+#include <asm/tbx/machine.inc>
+#include <asm/tbx/metagtbx.h>
+
+#define UNIT_BIT_MASK		TXUXXRXRQ_UXX_BITS
+#define REG_BIT_MASK		TXUXXRXRQ_RX_BITS
+#define THREAD_BIT_MASK		TXUXXRXRQ_TX_BITS
+
+#define UNIT_SHIFTS		TXUXXRXRQ_UXX_S
+#define REG_SHIFTS		TXUXXRXRQ_RX_S
+#define THREAD_SHIFTS		TXUXXRXRQ_TX_S
+
+#define UNIT_VAL(x)		(((x) << UNIT_SHIFTS) & UNIT_BIT_MASK)
+#define REG_VAL(x)		(((x) << REG_SHIFTS) & REG_BIT_MASK)
+#define THREAD_VAL(x)		(((x) << THREAD_SHIFTS) & THREAD_BIT_MASK)
+
+/*
+ * core_reg_write() - modify the content of a register in a core unit.
+ * @unit:	The unit to be modified.
+ * @reg:	Register number within the unit.
+ * @thread:	The thread we want to access.
+ * @val:	The new value to write.
+ *
+ * Check metac_x_y.inc for a list/defines of supported units (ie: TXUPC_ID,
+ * TXUTR_ID, etc), and metag.inc for the defines of regnums within the units
+ * (ie: TXMASKI_REGNUM, TXPOLLI_REGNUM, etc).
+ */
+void core_reg_write(int unit, int reg, int thread, unsigned int val)
+{
+	unsigned long flags;
+
+	/* TXUCT_ID has its own memory mapped registers */
+	if (unit == TXUCT_ID) {
+		void __iomem *cu_reg = __CU_addr(thread, reg);
+		writel(val, cu_reg);
+		return;
+	}
+
+	TBI_LOCK(flags);
+
+	/* wait for ready */
+	while (!(readl(TXUXXRXRQ) & TXUXXRXRQ_DREADY_BIT))
+		udelay(10);
+
+	/* set the value to write */
+	writel(val, TXUXXRXDT);
+
+	/* set the register to write */
+	val = UNIT_VAL(unit) | REG_VAL(reg) | THREAD_VAL(thread);
+	writel(val, TXUXXRXRQ);
+
+	/* wait for finish */
+	while (!(readl(TXUXXRXRQ) & TXUXXRXRQ_DREADY_BIT))
+		udelay(10);
+
+	TBI_UNLOCK(flags);
+}
+EXPORT_SYMBOL(core_reg_write);
+
+/*
+ * core_reg_read() - read the content of a register in a core unit.
+ * @unit:	The unit to be modified.
+ * @reg:	Register number within the unit.
+ * @thread:	The thread we want to access.
+ *
+ * Check metac_x_y.inc for a list/defines of supported units (ie: TXUPC_ID,
+ * TXUTR_ID, etc), and metag.inc for the defines of regnums within the units
+ * (ie: TXMASKI_REGNUM, TXPOLLI_REGNUM, etc).
+ */
+unsigned int core_reg_read(int unit, int reg, int thread)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	/* TXUCT_ID has its own memory mapped registers */
+	if (unit == TXUCT_ID) {
+		void __iomem *cu_reg = __CU_addr(thread, reg);
+		val = readl(cu_reg);
+		return val;
+	}
+
+	TBI_LOCK(flags);
+
+	/* wait for ready */
+	while (!(readl(TXUXXRXRQ) & TXUXXRXRQ_DREADY_BIT))
+		udelay(10);
+
+	/* set the register to read */
+	val = (UNIT_VAL(unit) | REG_VAL(reg) | THREAD_VAL(thread) |
+							TXUXXRXRQ_RDnWR_BIT);
+	writel(val, TXUXXRXRQ);
+
+	/* wait for finish */
+	while (!(readl(TXUXXRXRQ) & TXUXXRXRQ_DREADY_BIT))
+		udelay(10);
+
+	/* read the register value */
+	val = readl(TXUXXRXDT);
+
+	TBI_UNLOCK(flags);
+
+	return val;
+}
+EXPORT_SYMBOL(core_reg_read);
diff --git a/arch/metag/kernel/head.S b/arch/metag/kernel/head.S
index d0d8352..eb58032 100644
--- a/arch/metag/kernel/head.S
+++ b/arch/metag/kernel/head.S
@@ -43,3 +43,15 @@ __start:
 __exit:
 	XOR     TXENABLE,D0Re0,D0Re0
 	.size	__exit,.-__exit
+
+#ifdef CONFIG_SMP
+	.global _secondary_startup
+	.type _secondary_startup,function
+_secondary_startup:
+	MOVT	A0StP,#HI(_secondary_data_stack)
+	ADD	A0StP,A0StP,#LO(_secondary_data_stack)
+	GETD	A0StP,[A0StP]
+	ADD	A0StP,A0StP,#THREAD_INFO_SIZE
+	B	_secondary_start_kernel
+	.size	_secondary_startup,.-_secondary_startup
+#endif
diff --git a/arch/metag/kernel/smp.c b/arch/metag/kernel/smp.c
new file mode 100644
index 0000000..10940b1
--- /dev/null
+++ b/arch/metag/kernel/smp.c
@@ -0,0 +1,582 @@
+/*
+ *  Copyright (C) 2009,2010,2011 Imagination Technologies Ltd.
+ *
+ *  Copyright (C) 2002 ARM Limited, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/cache.h>
+#include <linux/profile.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/seq_file.h>
+#include <linux/irq.h>
+#include <linux/bootmem.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cachepart.h>
+#include <asm/core_reg.h>
+#include <asm/cpu.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/processor.h>
+#include <asm/setup.h>
+#include <asm/tlbflush.h>
+#include <asm/hwthread.h>
+#include <asm/traps.h>
+
+DECLARE_PER_CPU(PTBI, pTBI);
+
+void *secondary_data_stack;
+
+/*
+ * structures for inter-processor calls
+ * - A collection of single bit ipi messages.
+ */
+struct ipi_data {
+	spinlock_t lock;
+	unsigned long ipi_count;
+	unsigned long bits;
+};
+
+static DEFINE_PER_CPU(struct ipi_data, ipi_data) = {
+	.lock	= __SPIN_LOCK_UNLOCKED(ipi_data.lock),
+};
+
+static DEFINE_SPINLOCK(boot_lock);
+
+/*
+ * "thread" is assumed to be a valid Meta hardware thread ID.
+ */
+int __cpuinit boot_secondary(unsigned int thread, struct task_struct *idle)
+{
+	u32 val;
+
+	/*
+	 * set synchronisation state between this boot processor
+	 * and the secondary one
+	 */
+	spin_lock(&boot_lock);
+
+	core_reg_write(TXUPC_ID, 0, thread, (unsigned int)secondary_startup);
+	core_reg_write(TXUPC_ID, 1, thread, 0);
+
+	/*
+	 * Give the thread privilege (PSTAT) and clear potentially problematic
+	 * bits in the process (namely ISTAT, CBMarker, CBMarkerI, LSM_STEP).
+	 */
+	core_reg_write(TXUCT_ID, TXSTATUS_REGNUM, thread, TXSTATUS_PSTAT_BIT);
+
+	/* Clear the minim enable bit. */
+	val = core_reg_read(TXUCT_ID, TXPRIVEXT_REGNUM, thread);
+	core_reg_write(TXUCT_ID, TXPRIVEXT_REGNUM, thread, val & ~0x80);
+
+	/*
+	 * set the ThreadEnable bit (0x1) in the TXENABLE register
+	 * for the specified thread - off it goes!
+	 */
+	val = core_reg_read(TXUCT_ID, TXENABLE_REGNUM, thread);
+	core_reg_write(TXUCT_ID, TXENABLE_REGNUM, thread, val | 0x1);
+
+	/*
+	 * now the secondary core is starting up let it run its
+	 * calibrations, then wait for it to finish
+	 */
+	spin_unlock(&boot_lock);
+
+	return 0;
+}
+
+int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
+{
+	unsigned int thread = cpu_2_hwthread_id[cpu];
+	int ret;
+
+	load_pgd(swapper_pg_dir, thread);
+
+	flush_tlb_all();
+
+	/*
+	 * Tell the secondary CPU where to find its idle threads's stack.
+	 */
+	secondary_data_stack = task_stack_page(idle);
+
+	wmb();
+
+	/*
+	 * Now bring the CPU into our world.
+	 */
+	ret = boot_secondary(thread, idle);
+	if (ret == 0) {
+		unsigned long timeout;
+
+		/*
+		 * CPU was successfully started, wait for it
+		 * to come online or time out.
+		 */
+		timeout = jiffies + HZ;
+		while (time_before(jiffies, timeout)) {
+			if (cpu_online(cpu))
+				break;
+
+			udelay(10);
+			barrier();
+		}
+
+		if (!cpu_online(cpu))
+			ret = -EIO;
+	}
+
+	secondary_data_stack = NULL;
+
+	if (ret) {
+		pr_crit("CPU%u: processor failed to boot\n", cpu);
+
+		/*
+		 * FIXME: We need to clean up the new idle thread. --rmk
+		 */
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static DECLARE_COMPLETION(cpu_killed);
+
+/*
+ * __cpu_disable runs on the processor to be shutdown.
+ */
+int __cpuexit __cpu_disable(void)
+{
+	unsigned int cpu = smp_processor_id();
+	struct task_struct *p;
+
+	/*
+	 * Take this CPU offline.  Once we clear this, we can't return,
+	 * and we must not schedule until we're ready to give up the cpu.
+	 */
+	set_cpu_online(cpu, false);
+
+	/*
+	 * OK - migrate IRQs away from this CPU
+	 */
+	migrate_irqs();
+
+	/*
+	 * Flush user cache and TLB mappings, and then remove this CPU
+	 * from the vm mask set of all processes.
+	 */
+	flush_cache_all();
+	local_flush_tlb_all();
+
+	read_lock(&tasklist_lock);
+	for_each_process(p) {
+		if (p->mm)
+			cpumask_clear_cpu(cpu, mm_cpumask(p->mm));
+	}
+	read_unlock(&tasklist_lock);
+
+	return 0;
+}
+
+/*
+ * called on the thread which is asking for a CPU to be shutdown -
+ * waits until shutdown has completed, or it is timed out.
+ */
+void __cpuexit __cpu_die(unsigned int cpu)
+{
+	if (!wait_for_completion_timeout(&cpu_killed, msecs_to_jiffies(1)))
+		pr_err("CPU%u: unable to kill\n", cpu);
+}
+
+/*
+ * Called from the idle thread for the CPU which has been shutdown.
+ *
+ * Note that we do not return from this function. If this cpu is
+ * brought online again it will need to run secondary_startup().
+ */
+void __cpuexit cpu_die(void)
+{
+	local_irq_disable();
+	idle_task_exit();
+
+	complete(&cpu_killed);
+
+	__asm__("XOR	TXENABLE, D0Re0,D0Re0\n");
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+/*
+ * Called by both boot and secondaries to move global data into
+ * per-processor storage.
+ */
+void __cpuinit smp_store_cpu_info(unsigned int cpuid)
+{
+	struct cpuinfo_metag *cpu_info = &per_cpu(cpu_data, cpuid);
+
+	cpu_info->loops_per_jiffy = loops_per_jiffy;
+}
+
+/*
+ * This is the secondary CPU boot entry.  We're using this CPUs
+ * idle thread stack and the global page tables.
+ */
+asmlinkage void secondary_start_kernel(void)
+{
+	struct mm_struct *mm = &init_mm;
+	unsigned int cpu = smp_processor_id();
+
+	/*
+	 * All kernel threads share the same mm context; grab a
+	 * reference and switch to it.
+	 */
+	atomic_inc(&mm->mm_users);
+	atomic_inc(&mm->mm_count);
+	current->active_mm = mm;
+	cpumask_set_cpu(cpu, mm_cpumask(mm));
+	enter_lazy_tlb(mm, current);
+	local_flush_tlb_all();
+
+	/*
+	 * TODO: Some day it might be useful for each Linux CPU to
+	 * have its own TBI structure. That would allow each Linux CPU
+	 * to run different interrupt handlers for the same IRQ
+	 * number.
+	 *
+	 * For now, simply copying the pointer to the boot CPU's TBI
+	 * structure is sufficient because we always want to run the
+	 * same interrupt handler whatever CPU takes the interrupt.
+	 */
+	per_cpu(pTBI, cpu) = __TBI(TBID_ISTAT_BIT);
+
+	if (!per_cpu(pTBI, cpu))
+		panic("No TBI found!");
+
+	per_cpu_trap_init(cpu);
+
+	preempt_disable();
+
+	setup_txprivext();
+
+	/*
+	 * Enable local interrupts.
+	 */
+	tbi_startup_interrupt(TBID_SIGNUM_TRT);
+	notify_cpu_starting(cpu);
+	local_irq_enable();
+
+	/*
+	 * Start local timers for this CPU.
+	 */
+	local_timer_setup(cpu);
+
+	pr_info("CPU%u (thread %u): Booted secondary processor\n",
+		cpu, cpu_2_hwthread_id[cpu]);
+
+	calibrate_delay();
+	smp_store_cpu_info(cpu);
+
+	/*
+	 * OK, now it's safe to let the boot CPU continue
+	 */
+	notify_cpu_starting(cpu);
+
+	set_cpu_online(cpu, true);
+
+	/*
+	 * Check for cache aliasing.
+	 * Preemption is disabled
+	 */
+	check_for_cache_aliasing(cpu);
+
+	/*
+	 * OK, it's off to the idle thread for us
+	 */
+	cpu_idle();
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+	int cpu;
+	unsigned long bogosum = 0;
+
+	for_each_online_cpu(cpu)
+		bogosum += per_cpu(cpu_data, cpu).loops_per_jiffy;
+
+	pr_info("SMP: Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
+		num_online_cpus(),
+		bogosum / (500000/HZ),
+		(bogosum / (5000/HZ)) % 100);
+}
+
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+	unsigned int cpu = smp_processor_id();
+
+	init_new_context(current, &init_mm);
+	current_thread_info()->cpu = cpu;
+
+	smp_store_cpu_info(cpu);
+	init_cpu_present(cpu_possible_mask);
+}
+
+void __init smp_prepare_boot_cpu(void)
+{
+	unsigned int cpu = smp_processor_id();
+
+	per_cpu(pTBI, cpu) = __TBI(TBID_ISTAT_BIT);
+
+	if (!per_cpu(pTBI, cpu))
+		panic("No TBI found!");
+}
+
+static void smp_cross_call(cpumask_t callmap, enum ipi_msg_type msg);
+
+static void send_ipi_message(const struct cpumask *mask, enum ipi_msg_type msg)
+{
+	unsigned long flags;
+	unsigned int cpu;
+	cpumask_t map;
+
+	cpumask_clear(&map);
+	local_irq_save(flags);
+
+	for_each_cpu(cpu, mask) {
+		struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+
+		spin_lock(&ipi->lock);
+
+		/*
+		 * KICK interrupts are queued in hardware so we'll get
+		 * multiple interrupts if we call smp_cross_call()
+		 * multiple times for one msg. The problem is that we
+		 * only have one bit for each message - we can't queue
+		 * them in software.
+		 *
+		 * The first time through ipi_handler() we'll clear
+		 * the msg bit, having done all the work. But when we
+		 * return we'll get _another_ interrupt (and another,
+		 * and another until we've handled all the queued
+		 * KICKs). Running ipi_handler() when there's no work
+		 * to do is bad because that's how kick handler
+		 * chaining detects who the KICK was intended for.
+		 * See arch/metag/kernel/kick.c for more details.
+		 *
+		 * So only add 'cpu' to 'map' if we haven't already
+		 * queued a KICK interrupt for 'msg'.
+		 */
+		if (!(ipi->bits & (1 << msg))) {
+			ipi->bits |= 1 << msg;
+			cpumask_set_cpu(cpu, &map);
+		}
+
+		spin_unlock(&ipi->lock);
+	}
+
+	/*
+	 * Call the platform specific cross-CPU call function.
+	 */
+	smp_cross_call(map, msg);
+
+	local_irq_restore(flags);
+}
+
+void arch_send_call_function_ipi_mask(const struct cpumask *mask)
+{
+	send_ipi_message(mask, IPI_CALL_FUNC);
+}
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+	send_ipi_message(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
+}
+
+void show_ipi_list(struct seq_file *p)
+{
+	unsigned int cpu;
+
+	seq_puts(p, "IPI:");
+
+	for_each_present_cpu(cpu)
+		seq_printf(p, " %10lu", per_cpu(ipi_data, cpu).ipi_count);
+
+	seq_putc(p, '\n');
+}
+
+static DEFINE_SPINLOCK(stop_lock);
+
+/*
+ * Main handler for inter-processor interrupts
+ *
+ * For Meta, the ipimask now only identifies a single
+ * category of IPI (Bit 1 IPIs have been replaced by a
+ * different mechanism):
+ *
+ *  Bit 0 - Inter-processor function call
+ */
+static int do_IPI(struct pt_regs *regs)
+{
+	unsigned int cpu = smp_processor_id();
+	struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+	struct pt_regs *old_regs = set_irq_regs(regs);
+	unsigned long msgs, nextmsg;
+	int handled = 0;
+
+	ipi->ipi_count++;
+
+	spin_lock(&ipi->lock);
+	msgs = ipi->bits;
+	nextmsg = msgs & -msgs;
+	ipi->bits &= ~nextmsg;
+	spin_unlock(&ipi->lock);
+
+	if (nextmsg) {
+		handled = 1;
+
+		nextmsg = ffz(~nextmsg);
+		switch (nextmsg) {
+		case IPI_RESCHEDULE:
+			scheduler_ipi();
+			break;
+
+		case IPI_CALL_FUNC:
+			generic_smp_call_function_interrupt();
+			break;
+
+		case IPI_CALL_FUNC_SINGLE:
+			generic_smp_call_function_single_interrupt();
+			break;
+
+		default:
+			pr_crit("CPU%u: Unknown IPI message 0x%lx\n",
+				cpu, nextmsg);
+			break;
+		}
+	}
+
+	set_irq_regs(old_regs);
+
+	return handled;
+}
+
+void smp_send_reschedule(int cpu)
+{
+	send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
+}
+
+static void stop_this_cpu(void *data)
+{
+	unsigned int cpu = smp_processor_id();
+
+	if (system_state == SYSTEM_BOOTING ||
+	    system_state == SYSTEM_RUNNING) {
+		spin_lock(&stop_lock);
+		pr_crit("CPU%u: stopping\n", cpu);
+		dump_stack();
+		spin_unlock(&stop_lock);
+	}
+
+	set_cpu_online(cpu, false);
+
+	local_irq_disable();
+
+	hard_processor_halt(HALT_OK);
+}
+
+void smp_send_stop(void)
+{
+	smp_call_function(stop_this_cpu, NULL, 0);
+}
+
+/*
+ * not supported here
+ */
+int setup_profiling_timer(unsigned int multiplier)
+{
+	return -EINVAL;
+}
+
+/*
+ * We use KICKs for inter-processor interrupts.
+ *
+ * For every CPU in "callmap" the IPI data must already have been
+ * stored in that CPU's "ipi_data" member prior to calling this
+ * function.
+ */
+static void kick_raise_softirq(cpumask_t callmap, unsigned int irq)
+{
+	int cpu;
+
+	for_each_cpu(cpu, &callmap) {
+		unsigned int thread;
+
+		thread = cpu_2_hwthread_id[cpu];
+
+		BUG_ON(thread == BAD_HWTHREAD_ID);
+
+		writel(1, T0KICKI + (thread * TnXKICK_STRIDE));
+	}
+}
+
+static TBIRES ipi_handler(TBIRES State, int SigNum, int Triggers,
+		   int Inst, PTBI pTBI, int *handled)
+{
+	*handled = do_IPI((struct pt_regs *)State.Sig.pCtx);
+
+	return State;
+}
+
+static struct kick_irq_handler ipi_irq = {
+	.func = ipi_handler,
+};
+
+static void smp_cross_call(cpumask_t callmap, enum ipi_msg_type msg)
+{
+	kick_raise_softirq(callmap, 1);
+}
+
+static inline unsigned int get_core_count(void)
+{
+	int i;
+	unsigned int ret = 0;
+
+	for (i = 0; i < CONFIG_NR_CPUS; i++) {
+		if (core_reg_read(TXUCT_ID, TXENABLE_REGNUM, i))
+			ret++;
+	}
+
+	return ret;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+void __init smp_init_cpus(void)
+{
+	unsigned int i, ncores = get_core_count();
+
+	/* If no hwthread_map early param was set use default mapping */
+	for (i = 0; i < NR_CPUS; i++)
+		if (cpu_2_hwthread_id[i] == BAD_HWTHREAD_ID) {
+			cpu_2_hwthread_id[i] = i;
+			hwthread_id_2_cpu[i] = i;
+		}
+
+	for (i = 0; i < ncores; i++)
+		set_cpu_possible(i, true);
+
+	kick_register_func(&ipi_irq);
+}
diff --git a/arch/metag/kernel/topology.c b/arch/metag/kernel/topology.c
new file mode 100644
index 0000000..bec3dec
--- /dev/null
+++ b/arch/metag/kernel/topology.c
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (C) 2007  Paul Mundt
+ *  Copyright (C) 2010  Imagination Technolohies Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/node.h>
+#include <linux/nodemask.h>
+#include <linux/topology.h>
+
+#include <asm/cpu.h>
+
+DEFINE_PER_CPU(struct cpuinfo_metag, cpu_data);
+
+cpumask_t cpu_core_map[NR_CPUS];
+
+static cpumask_t cpu_coregroup_map(unsigned int cpu)
+{
+	return *cpu_possible_mask;
+}
+
+const struct cpumask *cpu_coregroup_mask(unsigned int cpu)
+{
+	return &cpu_core_map[cpu];
+}
+
+int arch_update_cpu_topology(void)
+{
+	unsigned int cpu;
+
+	for_each_possible_cpu(cpu)
+		cpu_core_map[cpu] = cpu_coregroup_map(cpu);
+
+	return 0;
+}
+
+static int __init topology_init(void)
+{
+	int i, ret;
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+	for_each_online_node(i)
+		register_one_node(i);
+#endif
+
+	for_each_present_cpu(i) {
+		struct cpuinfo_metag *cpuinfo = &per_cpu(cpu_data, i);
+#ifdef CONFIG_HOTPLUG_CPU
+		cpuinfo->cpu.hotpluggable = 1;
+#endif
+		ret = register_cpu(&cpuinfo->cpu, i);
+		if (unlikely(ret))
+			pr_warn("%s: register_cpu %d failed (%d)\n",
+				__func__, i, ret);
+	}
+
+#if defined(CONFIG_NUMA) && !defined(CONFIG_SMP)
+	/*
+	 * In the UP case, make sure the CPU association is still
+	 * registered under each node. Without this, sysfs fails
+	 * to make the connection between nodes other than node0
+	 * and cpu0.
+	 */
+	for_each_online_node(i)
+		if (i != numa_node_id())
+			register_cpu_under_node(raw_smp_processor_id(), i);
+#endif
+
+	return 0;
+}
+subsys_initcall(topology_init);
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 24/40] metag: DMA
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (20 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 23/40] metag: SMP support James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-11-09 14:25   ` Arnd Bergmann
  2012-10-31 16:14 ` [RFC PATCH v1 25/40] metag: optimised library functions James Hogan
                   ` (18 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add DMA mapping code.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/dma-mapping.h |  183 ++++++++++++
 arch/metag/kernel/dma.c              |  521 ++++++++++++++++++++++++++++++++++
 2 files changed, 704 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/dma-mapping.h
 create mode 100644 arch/metag/kernel/dma.c

diff --git a/arch/metag/include/asm/dma-mapping.h b/arch/metag/include/asm/dma-mapping.h
new file mode 100644
index 0000000..9a1b3ac
--- /dev/null
+++ b/arch/metag/include/asm/dma-mapping.h
@@ -0,0 +1,183 @@
+#ifndef _ASM_METAG_DMA_MAPPING_H
+#define _ASM_METAG_DMA_MAPPING_H
+
+#include <linux/mm.h>
+
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <linux/scatterlist.h>
+#include <asm/bug.h>
+
+#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)
+
+void *dma_alloc_coherent(struct device *dev, size_t size,
+			 dma_addr_t *dma_handle, gfp_t flag);
+
+void dma_free_coherent(struct device *dev, size_t size,
+		       void *vaddr, dma_addr_t dma_handle);
+
+void dma_sync_for_device(void *vaddr, size_t size, int dma_direction);
+void dma_sync_for_cpu(void *vaddr, size_t size, int dma_direction);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t dma_addr, size_t size);
+
+int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma,
+			  void *cpu_addr, dma_addr_t dma_addr, size_t size);
+
+static inline dma_addr_t
+dma_map_single(struct device *dev, void *ptr, size_t size,
+	       enum dma_data_direction direction)
+{
+	BUG_ON(!valid_dma_direction(direction));
+	WARN_ON(size == 0);
+	dma_sync_for_device(ptr, size, direction);
+	return virt_to_phys(ptr);
+}
+
+static inline void
+dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+		 enum dma_data_direction direction)
+{
+	BUG_ON(!valid_dma_direction(direction));
+	dma_sync_for_cpu((void *)bus_to_virt(dma_addr), size, direction);
+}
+
+static inline int
+dma_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
+	   enum dma_data_direction direction)
+{
+	struct scatterlist *sg;
+	int i;
+
+	BUG_ON(!valid_dma_direction(direction));
+	WARN_ON(nents == 0 || sglist[0].length == 0);
+
+	for_each_sg(sglist, sg, nents, i) {
+		BUG_ON(!sg_page(sg));
+
+		sg->dma_address = sg_phys(sg);
+		dma_sync_for_device(sg_virt(sg), sg->length, direction);
+	}
+
+	return nents;
+}
+
+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(!valid_dma_direction(direction));
+	dma_sync_for_device((void *)(page_to_phys(page) + offset), size,
+			    direction);
+	return page_to_phys(page) + offset;
+}
+
+static inline void
+dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
+	       enum dma_data_direction direction)
+{
+	BUG_ON(!valid_dma_direction(direction));
+	dma_sync_for_cpu((void *)bus_to_virt(dma_address), size, direction);
+}
+
+
+static inline void
+dma_unmap_sg(struct device *dev, struct scatterlist *sglist, int nhwentries,
+	     enum dma_data_direction direction)
+{
+	struct scatterlist *sg;
+	int i;
+
+	BUG_ON(!valid_dma_direction(direction));
+	WARN_ON(nhwentries == 0 || sglist[0].length == 0);
+
+	for_each_sg(sglist, sg, nhwentries, i) {
+		BUG_ON(!sg_page(sg));
+
+		sg->dma_address = sg_phys(sg);
+		dma_sync_for_cpu(sg_virt(sg), sg->length, 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)
+{
+	dma_sync_for_cpu((void *)bus_to_virt(dma_handle), size, 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)
+{
+	dma_sync_for_device((void *)bus_to_virt(dma_handle), size, direction);
+}
+
+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)
+{
+	dma_sync_for_cpu((void *)bus_to_virt(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)
+{
+	dma_sync_for_device((void *)bus_to_virt(dma_handle)+offset, size,
+			    direction);
+}
+
+static inline void
+dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
+		    enum dma_data_direction direction)
+{
+	int i;
+	for (i = 0; i < nelems; i++, sg++)
+		dma_sync_for_cpu(sg_virt(sg), sg->length, direction);
+}
+
+static inline void
+dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
+		       enum dma_data_direction direction)
+{
+	int i;
+	for (i = 0; i < nelems; i++, sg++)
+		dma_sync_for_device(sg_virt(sg), sg->length, direction);
+}
+
+static inline int
+dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
+{
+	return 0;
+}
+
+#define dma_supported(dev, mask)        (1)
+
+static inline int
+dma_set_mask(struct device *dev, u64 mask)
+{
+	if (!dev->dma_mask || !dma_supported(dev, mask))
+		return -EIO;
+
+	*dev->dma_mask = mask;
+
+	return 0;
+}
+
+/*
+ * dma_alloc_noncoherent() returns non-cacheable memory, so there's no need to
+ * do any flushing here.
+ */
+static inline void
+dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+	       enum dma_data_direction direction)
+{
+}
+
+#endif
diff --git a/arch/metag/kernel/dma.c b/arch/metag/kernel/dma.c
new file mode 100644
index 0000000..7e41f56
--- /dev/null
+++ b/arch/metag/kernel/dma.c
@@ -0,0 +1,521 @@
+/*
+ *  Meta version derived from arch/powerpc/lib/dma-noncoherent.c
+ *    Copyright (C) 2008 Imagination Technologies Ltd.
+ *
+ *  PowerPC version derived from arch/arm/mm/consistent.c
+ *    Copyright (C) 2001 Dan Malek (dmalek@jlc.net)
+ *
+ *  Copyright (C) 2000 Russell King
+ *
+ * Consistent memory allocators.  Used for DMA devices that want to
+ * share uncached memory with the processor core.  The function return
+ * is the virtual address and 'dma_handle' is the physical address.
+ * Mostly stolen from the ARM port, with some changes for PowerPC.
+ *						-- Dan
+ *
+ * Reorganized to get rid of the arch-specific consistent_* functions
+ * and provide non-coherent implementations for the DMA API. -Matt
+ *
+ * Added in_interrupt() safe dma_alloc_coherent()/dma_free_coherent()
+ * implementation. This is pulled straight from ARM and barely
+ * modified. -Matt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/highmem.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/static_key.h>
+
+#include <asm/tlbflush.h>
+#include <asm/mmu.h>
+
+#define CONSISTENT_OFFSET(x)	(((unsigned long)(x) - CONSISTENT_START) \
+					>> PAGE_SHIFT)
+
+static u64 get_coherent_dma_mask(struct device *dev)
+{
+	u64 mask = ~0ULL;
+
+	if (dev) {
+		mask = dev->coherent_dma_mask;
+
+		/*
+		 * Sanity check the DMA mask - it must be non-zero, and
+		 * must be able to be satisfied by a DMA allocation.
+		 */
+		if (mask == 0) {
+			dev_warn(dev, "coherent DMA mask is unset\n");
+			return 0;
+		}
+	}
+
+	return mask;
+}
+/*
+ * This is the page table (2MB) covering uncached, DMA consistent allocations
+ */
+static pte_t *consistent_pte;
+static DEFINE_SPINLOCK(consistent_lock);
+
+/*
+ * VM region handling support.
+ *
+ * This should become something generic, handling VM region allocations for
+ * vmalloc and similar (ioremap, module space, etc).
+ *
+ * I envisage vmalloc()'s supporting vm_struct becoming:
+ *
+ *  struct vm_struct {
+ *    struct metag_vm_region	region;
+ *    unsigned long	flags;
+ *    struct page	**pages;
+ *    unsigned int	nr_pages;
+ *    unsigned long	phys_addr;
+ *  };
+ *
+ * get_vm_area() would then call metag_vm_region_alloc with an appropriate
+ * struct metag_vm_region head (eg):
+ *
+ *  struct metag_vm_region vmalloc_head = {
+ *	.vm_list	= LIST_HEAD_INIT(vmalloc_head.vm_list),
+ *	.vm_start	= VMALLOC_START,
+ *	.vm_end		= VMALLOC_END,
+ *  };
+ *
+ * However, vmalloc_head.vm_start is variable (typically, it is dependent on
+ * the amount of RAM found at boot time.)  I would imagine that get_vm_area()
+ * would have to initialise this each time prior to calling
+ * metag_vm_region_alloc().
+ */
+struct metag_vm_region {
+	struct list_head vm_list;
+	unsigned long vm_start;
+	unsigned long vm_end;
+	struct page		*vm_pages;
+	int			vm_active;
+};
+
+static struct metag_vm_region consistent_head = {
+	.vm_list = LIST_HEAD_INIT(consistent_head.vm_list),
+	.vm_start = CONSISTENT_START,
+	.vm_end = CONSISTENT_END,
+};
+
+static struct metag_vm_region *metag_vm_region_alloc(struct metag_vm_region
+						     *head, size_t size,
+						     gfp_t gfp)
+{
+	unsigned long addr = head->vm_start, end = head->vm_end - size;
+	unsigned long flags;
+	struct metag_vm_region *c, *new;
+
+	new = kmalloc(sizeof(struct metag_vm_region), gfp);
+	if (!new)
+		goto out;
+
+	spin_lock_irqsave(&consistent_lock, flags);
+
+	list_for_each_entry(c, &head->vm_list, vm_list) {
+		if ((addr + size) < addr)
+			goto nospc;
+		if ((addr + size) <= c->vm_start)
+			goto found;
+		addr = c->vm_end;
+		if (addr > end)
+			goto nospc;
+	}
+
+found:
+	/*
+	 * Insert this entry _before_ the one we found.
+	 */
+	list_add_tail(&new->vm_list, &c->vm_list);
+	new->vm_start = addr;
+	new->vm_end = addr + size;
+	new->vm_active = 1;
+
+	spin_unlock_irqrestore(&consistent_lock, flags);
+	return new;
+
+nospc:
+	spin_unlock_irqrestore(&consistent_lock, flags);
+	kfree(new);
+out:
+	return NULL;
+}
+
+static struct metag_vm_region *metag_vm_region_find(struct metag_vm_region
+						    *head, unsigned long addr)
+{
+	struct metag_vm_region *c;
+
+	list_for_each_entry(c, &head->vm_list, vm_list) {
+		if (c->vm_active && c->vm_start == addr)
+			goto out;
+	}
+	c = NULL;
+out:
+	return c;
+}
+
+/*
+ * Allocate DMA-coherent memory space and return both the kernel remapped
+ * virtual and bus address for that space.
+ */
+void *dma_alloc_coherent(struct device *dev, size_t size,
+			 dma_addr_t *handle, gfp_t gfp)
+{
+	struct page *page;
+	struct metag_vm_region *c;
+	unsigned long order;
+	u64 mask = get_coherent_dma_mask(dev);
+	u64 limit;
+
+	if (!consistent_pte) {
+		pr_err("%s: not initialised\n", __func__);
+		dump_stack();
+		return NULL;
+	}
+
+	if (!mask)
+		goto no_page;
+	size = PAGE_ALIGN(size);
+	limit = (mask + 1) & ~mask;
+	if ((limit && size >= limit)
+	    || size >= (CONSISTENT_END - CONSISTENT_START)) {
+		pr_warn("coherent allocation too big (requested %#x mask %#Lx)\n",
+			size, mask);
+		return NULL;
+	}
+
+	order = get_order(size);
+
+	if (mask != 0xffffffff)
+		gfp |= GFP_DMA;
+
+	page = alloc_pages(gfp, order);
+	if (!page)
+		goto no_page;
+
+	/*
+	 * Invalidate any data that might be lurking in the
+	 * kernel direct-mapped region for device DMA.
+	 */
+	{
+		void *kaddr = page_address(page);
+		memset(kaddr, 0, size);
+		flush_dcache_region(kaddr, size);
+	}
+
+	/*
+	 * Allocate a virtual address in the consistent mapping region.
+	 */
+	c = metag_vm_region_alloc(&consistent_head, size,
+				  gfp & ~(__GFP_DMA | __GFP_HIGHMEM));
+	if (c) {
+		unsigned long vaddr = c->vm_start;
+		pte_t *pte = consistent_pte + CONSISTENT_OFFSET(vaddr);
+		struct page *end = page + (1 << order);
+
+		c->vm_pages = page;
+		split_page(page, order);
+
+		/*
+		 * Set the "dma handle"
+		 */
+		*handle = page_to_bus(page);
+
+		do {
+			BUG_ON(!pte_none(*pte));
+
+			SetPageReserved(page);
+			set_pte_at(&init_mm, vaddr,
+				   pte, mk_pte(page,
+					       pgprot_writecombine
+					       (PAGE_KERNEL)));
+			page++;
+			pte++;
+			vaddr += PAGE_SIZE;
+		} while (size -= PAGE_SIZE);
+
+		/*
+		 * Free the otherwise unused pages.
+		 */
+		while (page < end) {
+			__free_page(page);
+			page++;
+		}
+
+		return (void *)c->vm_start;
+	}
+
+	if (page)
+		__free_pages(page, order);
+no_page:
+	return NULL;
+}
+EXPORT_SYMBOL(dma_alloc_coherent);
+
+/*
+ * free a page as defined by the above mapping.
+ */
+void dma_free_coherent(struct device *dev, size_t size,
+		       void *vaddr, dma_addr_t dma_handle)
+{
+	struct metag_vm_region *c;
+	unsigned long flags, addr;
+	pte_t *ptep;
+
+	size = PAGE_ALIGN(size);
+
+	spin_lock_irqsave(&consistent_lock, flags);
+
+	c = metag_vm_region_find(&consistent_head, (unsigned long)vaddr);
+	if (!c)
+		goto no_area;
+
+	c->vm_active = 0;
+	if ((c->vm_end - c->vm_start) != size) {
+		pr_err("%s: freeing wrong coherent size (%ld != %d)\n",
+		       __func__, c->vm_end - c->vm_start, size);
+		dump_stack();
+		size = c->vm_end - c->vm_start;
+	}
+
+	ptep = consistent_pte + CONSISTENT_OFFSET(c->vm_start);
+	addr = c->vm_start;
+	do {
+		pte_t pte = ptep_get_and_clear(&init_mm, addr, ptep);
+		unsigned long pfn;
+
+		ptep++;
+		addr += PAGE_SIZE;
+
+		if (!pte_none(pte) && pte_present(pte)) {
+			pfn = pte_pfn(pte);
+
+			if (pfn_valid(pfn)) {
+				struct page *page = pfn_to_page(pfn);
+				ClearPageReserved(page);
+
+				__free_page(page);
+				continue;
+			}
+		}
+
+		pr_crit("%s: bad page in kernel page table\n",
+			__func__);
+	} while (size -= PAGE_SIZE);
+
+	flush_tlb_kernel_range(c->vm_start, c->vm_end);
+
+	list_del(&c->vm_list);
+
+	spin_unlock_irqrestore(&consistent_lock, flags);
+
+	kfree(c);
+	return;
+
+no_area:
+	spin_unlock_irqrestore(&consistent_lock, flags);
+	pr_err("%s: trying to free invalid coherent area: %p\n",
+	       __func__, vaddr);
+	dump_stack();
+}
+EXPORT_SYMBOL(dma_free_coherent);
+
+
+static int dma_mmap(struct device *dev, struct vm_area_struct *vma,
+		    void *cpu_addr, dma_addr_t dma_addr, size_t size)
+{
+	int ret = -ENXIO;
+
+	unsigned long flags, user_size, kern_size;
+	struct metag_vm_region *c;
+
+	user_size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+
+	spin_lock_irqsave(&consistent_lock, flags);
+	c = metag_vm_region_find(&consistent_head, (unsigned long)cpu_addr);
+	spin_unlock_irqrestore(&consistent_lock, flags);
+
+	if (c) {
+		unsigned long off = vma->vm_pgoff;
+
+		kern_size = (c->vm_end - c->vm_start) >> PAGE_SHIFT;
+
+		if (off < kern_size &&
+		    user_size <= (kern_size - off)) {
+			ret = remap_pfn_range(vma, vma->vm_start,
+					      page_to_pfn(c->vm_pages) + off,
+					      user_size << PAGE_SHIFT,
+					      vma->vm_page_prot);
+		}
+	}
+
+
+	return ret;
+}
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t dma_addr, size_t size)
+{
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	return dma_mmap(dev, vma, cpu_addr, dma_addr, size);
+}
+EXPORT_SYMBOL(dma_mmap_coherent);
+
+int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma,
+			  void *cpu_addr, dma_addr_t dma_addr, size_t size)
+{
+	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+	return dma_mmap(dev, vma, cpu_addr, dma_addr, size);
+}
+EXPORT_SYMBOL(dma_mmap_writecombine);
+
+
+
+
+/*
+ * Initialise the consistent memory allocation.
+ */
+static int __init dma_alloc_init(void)
+{
+	pgd_t *pgd, *pgd_k;
+	pud_t *pud, *pud_k;
+	pmd_t *pmd, *pmd_k;
+	pte_t *pte;
+	int ret = 0;
+
+	do {
+		int offset = pgd_index(CONSISTENT_START);
+		pgd = pgd_offset(&init_mm, CONSISTENT_START);
+		pud = pud_alloc(&init_mm, pgd, CONSISTENT_START);
+		pmd = pmd_alloc(&init_mm, pud, CONSISTENT_START);
+		if (!pmd) {
+			pr_err("%s: no pmd tables\n", __func__);
+			ret = -ENOMEM;
+			break;
+		}
+		WARN_ON(!pmd_none(*pmd));
+
+		pte = pte_alloc_kernel(pmd, CONSISTENT_START);
+		if (!pte) {
+			pr_err("%s: no pte tables\n", __func__);
+			ret = -ENOMEM;
+			break;
+		}
+
+		pgd_k = ((pgd_t *) mmu_get_base()) + offset;
+		pud_k = pud_offset(pgd_k, CONSISTENT_START);
+		pmd_k = pmd_offset(pud_k, CONSISTENT_START);
+		set_pmd(pmd_k, *pmd);
+
+		consistent_pte = pte;
+	} while (0);
+
+	return ret;
+}
+early_initcall(dma_alloc_init);
+
+#ifdef CONFIG_META_L2C
+/* Invalidate (may also write back if necessary) */
+static inline void invalidate_dcache_region(void *start, unsigned long size)
+{
+	if (meta_l2c_has_invalidate())
+		cachew_region_op(start, size, CACHEW_INVALIDATE_L1D_L2);
+	else
+		flush_dcache_region(start, size);
+}
+#else
+#define invalidate_dcache_region(s, l)	flush_dcache_region((s), (l))
+#endif
+
+/*
+ * make an area consistent to devices.
+ */
+void dma_sync_for_device(void *vaddr, size_t size, int dma_direction)
+{
+	/*
+	 * Ensure any writes get through the write combiner. This is necessary
+	 * even with DMA_FROM_DEVICE, or the write may dirty the cache after
+	 * we've invalidated it and get written back during the DMA.
+	 */
+
+	barrier();
+
+	switch (dma_direction) {
+	case DMA_BIDIRECTIONAL:
+		/*
+		 * Writeback to ensure the device can see our latest changes and
+		 * so that we have no dirty lines, and invalidate the cache
+		 * lines too in preparation for receiving the buffer back
+		 * (dma_sync_for_cpu) later.
+		 */
+		flush_dcache_region(vaddr, size);
+		break;
+	case DMA_TO_DEVICE:
+		/*
+		 * Writeback to ensure the device can see our latest changes.
+		 * There's no need to invalidate as the device shouldn't write
+		 * to the buffer.
+		 */
+		writeback_dcache_region(vaddr, size);
+		break;
+	case DMA_FROM_DEVICE:
+		/*
+		 * Invalidate to ensure we have no dirty lines that could get
+		 * written back during the DMA. It's also safe to flush
+		 * (writeback) here if necessary.
+		 */
+		invalidate_dcache_region(vaddr, size);
+		break;
+	case DMA_NONE:
+		BUG();
+	}
+
+	wmb();
+}
+EXPORT_SYMBOL(dma_sync_for_device);
+
+/*
+ * make an area consistent to the core.
+ */
+void dma_sync_for_cpu(void *vaddr, size_t size, int dma_direction)
+{
+	/*
+	 * Hardware L2 cache prefetch doesn't occur across 4K physical
+	 * boundaries, however according to Documentation/DMA-API-HOWTO.txt
+	 * kmalloc'd memory is DMA'able, so accesses in nearby memory could
+	 * trigger a cache fill in the DMA buffer.
+	 *
+	 * This should never cause dirty lines, so a flush or invalidate should
+	 * be safe to allow us to see data from the device.
+	 */
+	if (_meta_l2c_pf_is_enabled()) {
+		switch (dma_direction) {
+		case DMA_BIDIRECTIONAL:
+		case DMA_FROM_DEVICE:
+			invalidate_dcache_region(vaddr, size);
+			break;
+		case DMA_TO_DEVICE:
+			/* The device shouldn't have written to the buffer */
+			break;
+		case DMA_NONE:
+			BUG();
+		}
+	}
+
+	rmb();
+}
+EXPORT_SYMBOL(dma_sync_for_cpu);
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 25/40] metag: optimised library functions
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (21 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 24/40] metag: DMA James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 26/40] metag: Stack unwinding James Hogan
                   ` (17 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add optimised library functions for metag.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/checksum.h |   92 +++
 arch/metag/include/asm/div64.h    |   12 +
 arch/metag/include/asm/string.h   |   13 +
 arch/metag/lib/ashldi3.S          |   33 +
 arch/metag/lib/ashrdi3.S          |   33 +
 arch/metag/lib/checksum.c         |  169 +++++
 arch/metag/lib/clear_page.S       |   17 +
 arch/metag/lib/copy_page.S        |   20 +
 arch/metag/lib/delay.c            |   55 ++
 arch/metag/lib/div64.S            |  108 +++
 arch/metag/lib/divsi3.S           |  100 +++
 arch/metag/lib/ip_fast_csum.S     |   32 +
 arch/metag/lib/lshrdi3.S          |   33 +
 arch/metag/lib/memcpy.S           |  185 +++++
 arch/metag/lib/memmove.S          |  345 ++++++++++
 arch/metag/lib/memset.S           |   86 +++
 arch/metag/lib/modsi3.S           |   38 +
 arch/metag/lib/muldi3.S           |   44 ++
 arch/metag/lib/usercopy.c         | 1341 +++++++++++++++++++++++++++++++++++++
 19 files changed, 2756 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/checksum.h
 create mode 100644 arch/metag/include/asm/div64.h
 create mode 100644 arch/metag/include/asm/string.h
 create mode 100644 arch/metag/lib/ashldi3.S
 create mode 100644 arch/metag/lib/ashrdi3.S
 create mode 100644 arch/metag/lib/checksum.c
 create mode 100644 arch/metag/lib/clear_page.S
 create mode 100644 arch/metag/lib/copy_page.S
 create mode 100644 arch/metag/lib/delay.c
 create mode 100644 arch/metag/lib/div64.S
 create mode 100644 arch/metag/lib/divsi3.S
 create mode 100644 arch/metag/lib/ip_fast_csum.S
 create mode 100644 arch/metag/lib/lshrdi3.S
 create mode 100644 arch/metag/lib/memcpy.S
 create mode 100644 arch/metag/lib/memmove.S
 create mode 100644 arch/metag/lib/memset.S
 create mode 100644 arch/metag/lib/modsi3.S
 create mode 100644 arch/metag/lib/muldi3.S
 create mode 100644 arch/metag/lib/usercopy.c

diff --git a/arch/metag/include/asm/checksum.h b/arch/metag/include/asm/checksum.h
new file mode 100644
index 0000000..1113f4b
--- /dev/null
+++ b/arch/metag/include/asm/checksum.h
@@ -0,0 +1,92 @@
+#ifndef _METAG_CHECKSUM_H
+#define _METAG_CHECKSUM_H
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+extern __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
+ */
+extern __wsum csum_partial_copy(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);
+
+#define csum_partial_copy_nocheck(src, dst, len, sum)	\
+	csum_partial_copy((src), (dst), (len), (sum))
+
+/*
+ * Fold a partial checksum
+ */
+static inline __sum16 csum_fold(__wsum csum)
+{
+	u32 sum = (__force u32)csum;
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+	return (__force __sum16)~sum;
+}
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ */
+extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl);
+
+/*
+ * 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)
+{
+	unsigned long len_proto = (proto + len) << 8;
+	__asm__("ADD    %0, %0, %1\n"
+		"ADDS   %0, %0, %2\n"
+		"ADDCS  %0, %0, #1\n"
+		"ADDS   %0, %0, %3\n"
+		"ADDCS  %0, %0, #1\n"
+		: "=d"(sum)
+		: "d"(daddr), "d"(saddr), "d"(len_proto),
+		  "0"(sum)
+		: "cc");
+	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);
+
+#endif /* _METAG_CHECKSUM_H */
diff --git a/arch/metag/include/asm/div64.h b/arch/metag/include/asm/div64.h
new file mode 100644
index 0000000..0fdd116
--- /dev/null
+++ b/arch/metag/include/asm/div64.h
@@ -0,0 +1,12 @@
+#ifndef __ASM_DIV64_H__
+#define __ASM_DIV64_H__
+
+#include <asm-generic/div64.h>
+
+extern u64 div_u64(u64 dividend, u64 divisor);
+extern s64 div_s64(s64 dividend, s64 divisor);
+
+#define div_u64 div_u64
+#define div_s64 div_s64
+
+#endif
diff --git a/arch/metag/include/asm/string.h b/arch/metag/include/asm/string.h
new file mode 100644
index 0000000..53e3806
--- /dev/null
+++ b/arch/metag/include/asm/string.h
@@ -0,0 +1,13 @@
+#ifndef _METAG_STRING_H_
+#define _METAG_STRING_H_
+
+#define __HAVE_ARCH_MEMSET
+extern void *memset(void *__s, int __c, size_t __count);
+
+#define __HAVE_ARCH_MEMCPY
+void *memcpy(void *__to, __const__ void *__from, size_t __n);
+
+#define __HAVE_ARCH_MEMMOVE
+extern void *memmove(void *__dest, __const__ void *__src, size_t __n);
+
+#endif /* _METAG_STRING_H_ */
diff --git a/arch/metag/lib/ashldi3.S b/arch/metag/lib/ashldi3.S
new file mode 100644
index 0000000..78d6974
--- /dev/null
+++ b/arch/metag/lib/ashldi3.S
@@ -0,0 +1,33 @@
+! Copyright (C) 2012 by Imagination Technologies Ltd.
+!
+! 64-bit arithmetic shift left routine.
+!
+
+	.text
+	.global ___ashldi3
+	.type   ___ashldi3,function
+
+___ashldi3:
+	MOV     D0Re0,D0Ar2
+	MOV     D1Re0,D1Ar1
+	CMP     D1Ar3,#0                ! COUNT == 0
+	MOVEQ   PC,D1RtP                ! Yes, return
+
+	SUBS    D0Ar4,D1Ar3,#32         ! N = COUNT - 32
+	BGE     $L10
+
+!! Shift < 32
+	NEG     D0Ar4,D0Ar4             ! N = - N
+	LSL     D1Re0,D1Re0,D1Ar3       ! HI = HI << COUNT
+	LSR     D0Ar6,D0Re0,D0Ar4       ! TMP= LO >> -(COUNT - 32)
+	OR      D1Re0,D1Re0,D0Ar6       ! HI = HI | TMP
+	SWAP    D0Ar4,D1Ar3
+	LSL     D0Re0,D0Re0,D0Ar4       ! LO = LO << COUNT
+	MOV     PC,D1RtP
+
+$L10:
+!! Shift >= 32
+	LSL     D1Re0,D0Re0,D0Ar4       ! HI = LO << N
+	MOV     D0Re0,#0                ! LO = 0
+	MOV     PC,D1RtP
+	.size ___ashldi3,.-___ashldi3
diff --git a/arch/metag/lib/ashrdi3.S b/arch/metag/lib/ashrdi3.S
new file mode 100644
index 0000000..7cb7ed3
--- /dev/null
+++ b/arch/metag/lib/ashrdi3.S
@@ -0,0 +1,33 @@
+! Copyright (C) 2012 by Imagination Technologies Ltd.
+!
+! 64-bit arithmetic shift right routine.
+!
+
+	.text
+	.global ___ashrdi3
+	.type   ___ashrdi3,function
+
+___ashrdi3:
+	MOV     D0Re0,D0Ar2
+	MOV     D1Re0,D1Ar1
+	CMP     D1Ar3,#0                ! COUNT == 0
+	MOVEQ   PC,D1RtP                ! Yes, return
+
+	MOV     D0Ar4,D1Ar3
+	SUBS    D1Ar3,D1Ar3,#32         ! N = COUNT - 32
+	BGE     $L20
+
+!! Shift < 32
+	NEG     D1Ar3,D1Ar3             ! N = - N
+	LSR     D0Re0,D0Re0,D0Ar4       ! LO = LO >> COUNT
+	LSL     D0Ar6,D1Re0,D1Ar3       ! TMP= HI << -(COUNT - 32)
+	OR      D0Re0,D0Re0,D0Ar6       ! LO = LO | TMP
+	SWAP    D1Ar3,D0Ar4
+	ASR     D1Re0,D1Re0,D1Ar3       ! HI = HI >> COUNT
+	MOV     PC,D1RtP
+$L20:
+!! Shift >= 32
+	ASR     D0Re0,D1Re0,D1Ar3       ! LO = HI >> N
+	ASR     D1Re0,D1Re0,#31         ! HI = HI >> 31
+	MOV     PC,D1RtP
+	.size ___ashrdi3,.-___ashrdi3
diff --git a/arch/metag/lib/checksum.c b/arch/metag/lib/checksum.c
new file mode 100644
index 0000000..ac2daa2
--- /dev/null
+++ b/arch/metag/lib/checksum.c
@@ -0,0 +1,169 @@
+/*
+ *
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		IP/TCP/UDP checksumming routines
+ *
+ * Authors:	Jorge Cwik, <jorge@laser.satlink.net>
+ *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *		Tom May, <ftom@netcom.com>
+ *		Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
+ *		Lots of code moved from tcp.c and ip.c; see those files
+ *		for more names.
+ *
+ * 03/02/96	Jes Sorensen, Andreas Schwab, Roman Hodek:
+ *		Fixed some nasty bugs, causing some horrible crashes.
+ *		A: At some points, the sum (%0) was used as
+ *		length-counter instead of the length counter
+ *		(%1). Thanks to Roman Hodek for pointing this out.
+ *		B: GCC seems to mess up if one uses too many
+ *		data-registers to hold input values and one tries to
+ *		specify d0 and d1 as scratch registers. Letting gcc
+ *		choose these registers itself solves the problem.
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+
+/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
+ kills, so most of the assembly has to go. */
+
+#include <linux/module.h>
+#include <net/checksum.h>
+
+#include <asm/byteorder.h>
+
+static inline unsigned short from32to16(unsigned int x)
+{
+	/* add up 16-bit and 16-bit for 16+c bit */
+	x = (x & 0xffff) + (x >> 16);
+	/* add up carry.. */
+	x = (x & 0xffff) + (x >> 16);
+	return x;
+}
+
+static unsigned int do_csum(const unsigned char *buff, int len)
+{
+	int odd;
+	unsigned int result = 0;
+
+	if (len <= 0)
+		goto out;
+	odd = 1 & (unsigned long) buff;
+	if (odd) {
+#ifdef __LITTLE_ENDIAN
+		result += (*buff << 8);
+#else
+		result = *buff;
+#endif
+		len--;
+		buff++;
+	}
+	if (len >= 2) {
+		if (2 & (unsigned long) buff) {
+			result += *(unsigned short *) buff;
+			len -= 2;
+			buff += 2;
+		}
+		if (len >= 4) {
+			const unsigned char *end = buff + ((unsigned)len & ~3);
+			unsigned int carry = 0;
+			do {
+				unsigned int w = *(unsigned int *) buff;
+				buff += 4;
+				result += carry;
+				result += w;
+				carry = (w > result);
+			} while (buff < end);
+			result += carry;
+			result = (result & 0xffff) + (result >> 16);
+		}
+		if (len & 2) {
+			result += *(unsigned short *) buff;
+			buff += 2;
+		}
+	}
+	if (len & 1)
+#ifdef __LITTLE_ENDIAN
+		result += *buff;
+#else
+		result += (*buff << 8);
+#endif
+	result = from32to16(result);
+	if (odd)
+		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+	return result;
+}
+
+EXPORT_SYMBOL(ip_fast_csum);
+
+/*
+ * 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 wsum)
+{
+	unsigned int sum = (__force unsigned int)wsum;
+	unsigned int result = do_csum(buff, len);
+
+	/* add in old sum, and carry.. */
+	result += sum;
+	if (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);
+}
+EXPORT_SYMBOL(ip_compute_csum);
+
+/*
+ * 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)
+{
+	int missing;
+
+	missing = __copy_from_user(dst, src, len);
+	if (missing) {
+		memset(dst + len - missing, 0, missing);
+		*csum_err = -EFAULT;
+	} else
+		*csum_err = 0;
+
+	return csum_partial(dst, len, sum);
+}
+EXPORT_SYMBOL(csum_partial_copy_from_user);
+
+/*
+ * copy from ds while checksumming, otherwise like csum_partial
+ */
+__wsum
+csum_partial_copy(const void *src, void *dst, int len, __wsum sum)
+{
+	memcpy(dst, src, len);
+	return csum_partial(dst, len, sum);
+}
+EXPORT_SYMBOL(csum_partial_copy);
diff --git a/arch/metag/lib/clear_page.S b/arch/metag/lib/clear_page.S
new file mode 100644
index 0000000..43144ee
--- /dev/null
+++ b/arch/metag/lib/clear_page.S
@@ -0,0 +1,17 @@
+        ! Copyright 2007,2008,2009 Imagination Technologies Ltd.
+
+#include <asm/page.h>
+
+        .text
+        .global	_clear_page
+        .type   _clear_page,function
+	!! D1Ar1 - page
+_clear_page:
+	MOV  TXRPT,#((PAGE_SIZE / 8) - 1)
+	MOV  D0Re0,#0
+	MOV  D1Re0,#0
+$Lclear_page_loop:
+	SETL [D1Ar1++],D0Re0,D1Re0
+	BR   $Lclear_page_loop
+	MOV  PC,D1RtP
+        .size	_clear_page,.-_clear_page
diff --git a/arch/metag/lib/copy_page.S b/arch/metag/lib/copy_page.S
new file mode 100644
index 0000000..91f7d46
--- /dev/null
+++ b/arch/metag/lib/copy_page.S
@@ -0,0 +1,20 @@
+        ! Copyright 2007,2008 Imagination Technologies Ltd.
+
+#include <asm/page.h>
+
+        .text
+        .global	_copy_page
+        .type   _copy_page,function
+	!! D1Ar1 - to
+	!! D0Ar2 - from
+_copy_page:
+	MOV  D0FrT,#PAGE_SIZE
+$Lcopy_page_loop:
+	GETL D0Re0,D1Re0,[D0Ar2++]
+	GETL D0Ar6,D1Ar5,[D0Ar2++]
+	SETL [D1Ar1++],D0Re0,D1Re0
+	SETL [D1Ar1++],D0Ar6,D1Ar5
+	SUBS D0FrT,D0FrT,#16
+	BNZ  $Lcopy_page_loop
+	MOV  PC,D1RtP
+        .size	_copy_page,.-_copy_page
diff --git a/arch/metag/lib/delay.c b/arch/metag/lib/delay.c
new file mode 100644
index 0000000..e1cfcbb
--- /dev/null
+++ b/arch/metag/lib/delay.c
@@ -0,0 +1,55 @@
+/*
+ *	Precise Delay Loops for Meta
+ *
+ *	Copyright (C) 1993 Linus Torvalds
+ *	Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *	Copyright (C) 2007,2009 Imagination Technologies Ltd.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+
+#include <asm/processor.h>
+
+/*
+ * TXTACTCYC is only 24 bits, so on chips with fast clocks it will wrap
+ * many times per-second. If it does wrap __delay will return prematurely,
+ * but this is only likely with large delay values.
+ *
+ * We also can't implement read_current_timer() with TXTACTCYC due to
+ * this wrapping behaviour.
+ */
+#define rdtimer(t) asm volatile ("MOV %0,TXACTCYC\n" : "=r" (t));
+
+void __delay(unsigned long loops)
+{
+	unsigned long bclock, now;
+
+	rdtimer(bclock);
+	do {
+		asm("NOP");
+		rdtimer(now);
+	} while ((now-bclock) < loops);
+}
+EXPORT_SYMBOL(__delay);
+
+inline void __const_udelay(unsigned long xloops)
+{
+	u64 loops = (u64)xloops * (u64)loops_per_jiffy * HZ;
+	__delay(loops >> 32);
+}
+EXPORT_SYMBOL(__const_udelay);
+
+void __udelay(unsigned long usecs)
+{
+	__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
+}
+EXPORT_SYMBOL(__udelay);
+
+void __ndelay(unsigned long nsecs)
+{
+	__const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
+}
+EXPORT_SYMBOL(__ndelay);
diff --git a/arch/metag/lib/div64.S b/arch/metag/lib/div64.S
new file mode 100644
index 0000000..1cfc934
--- /dev/null
+++ b/arch/metag/lib/div64.S
@@ -0,0 +1,108 @@
+! Copyright (C) 2012 Imagination Technologies Ltd.
+!
+! Signed/unsigned 64-bit division routines.
+!
+
+	.text
+	.global _div_u64
+	.type   _div_u64,function
+
+_div_u64:
+$L1:
+	ORS     A0.3,D1Ar3,D0Ar4
+	BNE     $L3
+$L2:
+	MOV     D0Re0,D0Ar2
+	MOV     D1Re0,D1Ar1
+	MOV     PC,D1RtP
+$L3:
+	CMP     D1Ar3,D1Ar1
+	CMPEQ   D0Ar4,D0Ar2
+	MOV     D0Re0,#1
+	MOV     D1Re0,#0
+	BHS     $L6
+$L4:
+	ADDS    D0Ar6,D0Ar4,D0Ar4
+	ADD     D1Ar5,D1Ar3,D1Ar3
+	ADDCS   D1Ar5,D1Ar5,#1
+	CMP     D1Ar5,D1Ar3
+	CMPEQ   D0Ar6,D0Ar4
+	BLO     $L6
+$L5:
+	MOV     D0Ar4,D0Ar6
+	MOV     D1Ar3,D1Ar5
+	ADDS    D0Re0,D0Re0,D0Re0
+	ADD     D1Re0,D1Re0,D1Re0
+	ADDCS   D1Re0,D1Re0,#1
+	CMP     D1Ar3,D1Ar1
+	CMPEQ   D0Ar4,D0Ar2
+	BLO     $L4
+$L6:
+	ORS     A0.3,D1Re0,D0Re0
+	MOV     D0Ar6,#0
+	MOV     D1Ar5,D0Ar6
+	BEQ     $L10
+$L7:
+	CMP     D1Ar1,D1Ar3
+	CMPEQ   D0Ar2,D0Ar4
+	BLO     $L9
+$L8:
+	ADDS    D0Ar6,D0Ar6,D0Re0
+	ADD     D1Ar5,D1Ar5,D1Re0
+	ADDCS   D1Ar5,D1Ar5,#1
+
+	SUBS    D0Ar2,D0Ar2,D0Ar4
+	SUB     D1Ar1,D1Ar1,D1Ar3
+	SUBCS   D1Ar1,D1Ar1,#1
+$L9:
+	LSL     A0.3,D1Re0,#31
+	LSR     D0Re0,D0Re0,#1
+	LSR     D1Re0,D1Re0,#1
+	OR      D0Re0,D0Re0,A0.3
+	LSL     A0.3,D1Ar3,#31
+	LSR     D0Ar4,D0Ar4,#1
+	LSR     D1Ar3,D1Ar3,#1
+	OR      D0Ar4,D0Ar4,A0.3
+	ORS     A0.3,D1Re0,D0Re0
+	BNE     $L7
+$L10:
+	MOV     D0Re0,D0Ar6
+	MOV     D1Re0,D1Ar5
+	MOV     PC,D1RtP
+	.size _div_u64,.-_div_u64
+
+	.text
+	.global _div_s64
+	.type   _div_s64,function
+_div_s64:
+	MSETL   [A0StP],D0FrT,D0.5
+	XOR     D0.5,D0Ar2,D0Ar4
+	XOR     D1.5,D1Ar1,D1Ar3
+	TSTT    D1Ar1,#HI(0x80000000)
+	BZ      $L25
+
+	NEGS    D0Ar2,D0Ar2
+	NEG     D1Ar1,D1Ar1
+	SUBCS   D1Ar1,D1Ar1,#1
+$L25:
+	TSTT    D1Ar3,#HI(0x80000000)
+	BZ      $L27
+
+	NEGS    D0Ar4,D0Ar4
+	NEG     D1Ar3,D1Ar3
+	SUBCS   D1Ar3,D1Ar3,#1
+$L27:
+	CALLR   D1RtP,_div_u64
+	TSTT    D1.5,#HI(0x80000000)
+	BZ      $L29
+
+	NEGS    D0Re0,D0Re0
+	NEG     D1Re0,D1Re0
+	SUBCS   D1Re0,D1Re0,#1
+$L29:
+
+	GETL    D0FrT,D1RtP,[A0StP+#(-16)]
+	GETL    D0.5,D1.5,[A0StP+#(-8)]
+	SUB     A0StP,A0StP,#16
+	MOV     PC,D1RtP
+	.size _div_s64,.-_div_s64
diff --git a/arch/metag/lib/divsi3.S b/arch/metag/lib/divsi3.S
new file mode 100644
index 0000000..7c8a8ae
--- /dev/null
+++ b/arch/metag/lib/divsi3.S
@@ -0,0 +1,100 @@
+! Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
+!               Imagination Technologies Ltd
+!
+! Integer divide routines.
+!
+
+	.text
+	.global ___udivsi3
+	.type   ___udivsi3,function
+	.align  2
+___udivsi3:
+!!
+!! Since core is signed divide case, just set control variable
+!!
+	MOV     D1Re0,D0Ar2             ! Au already in A1Ar1, Bu -> D1Re0
+	MOV     D0Re0,#0                ! Result is 0
+	MOV     D0Ar4,#0                ! Return positive result
+	B       $LIDMCUStart
+	.size   ___udivsi3,.-___udivsi3
+
+!!
+!! 32-bit division signed i/p - passed signed 32-bit numbers
+!!
+	.global ___divsi3
+	.type   ___divsi3,function
+	.align  2
+___divsi3:
+!!
+!! A already in D1Ar1, B already in D0Ar2 -> make B abs(B)
+!!
+	MOV     D1Re0,D0Ar2             ! A already in A1Ar1, B -> D1Re0
+	MOV     D0Re0,#0                ! Result is 0
+	XOR     D0Ar4,D1Ar1,D1Re0       ! D0Ar4 -ive if result is -ive
+	ABS     D1Ar1,D1Ar1             ! abs(A) -> Au
+	ABS     D1Re0,D1Re0             ! abs(B) -> Bu
+$LIDMCUStart:
+	CMP     D1Ar1,D1Re0             ! Is ( Au > Bu )?
+	LSR     D1Ar3,D1Ar1,#2          ! Calculate (Au & (~3)) >> 2
+	CMPHI   D1Re0,D1Ar3             ! OR ( (Au & (~3)) <= (Bu << 2) )?
+	LSLSHI  D1Ar3,D1Re0,#1          ! Buq = Bu << 1
+	BLS     $LIDMCUSetup            ! Yes: Do normal divide
+!!
+!! Quick divide setup can assume that CurBit only needs to start at 2
+!!
+$LIDMCQuick:
+	CMP     D1Ar1,D1Ar3             ! ( A >= Buq )?
+	ADDCC   D0Re0,D0Re0,#2          ! If yes result += 2
+	SUBCC   D1Ar1,D1Ar1,D1Ar3       !        and A -= Buq
+	CMP     D1Ar1,D1Re0             ! ( A >= Bu )?
+	ADDCC   D0Re0,D0Re0,#1          ! If yes result += 1
+	SUBCC   D1Ar1,D1Ar1,D1Re0       !        and A -= Bu
+	ORS     D0Ar4,D0Ar4,D0Ar4       ! Return neg result?
+	NEG     D0Ar2,D0Re0             ! Calulate neg result
+	MOVMI   D0Re0,D0Ar2             ! Yes: Take neg result
+$LIDMCRet:
+	MOV     PC,D1RtP
+!!
+!!  Setup for general unsigned divide code
+!!
+!!      D0Re0 is used to form the result, already set to Zero
+!!      D1Re0 is the input Bu value, this gets trashed
+!!      D0Ar6 is curbit which is set to 1 at the start and shifted up
+!!      D0Ar4 is negative if we should return a negative result
+!!      D1Ar1 is the input Au value, eventually this holds the remainder
+!!
+$LIDMCUSetup:
+	CMP     D1Ar1,D1Re0             ! Is ( Au < Bu )?
+	MOV     D0Ar6,#1                ! Set curbit to 1
+	BCS     $LIDMCRet               ! Yes: Return 0 remainder Au
+!!
+!! Calculate alignment using FFB instruction
+!!
+	FFB     D1Ar5,D1Ar1             ! Find first bit of Au
+	ANDN    D1Ar5,D1Ar5,#31         ! Handle exceptional case.
+	ORN     D1Ar5,D1Ar5,#31         ! if N bit set, set to 31
+	FFB     D1Ar3,D1Re0             ! Find first bit of Bu
+	ANDN    D1Ar3,D1Ar3,#31         ! Handle exceptional case.
+	ORN     D1Ar3,D1Ar3,#31         ! if N bit set, set to 31
+	SUBS    D1Ar3,D1Ar5,D1Ar3       ! calculate diff, ffbA - ffbB
+	MOV     D0Ar2,D1Ar3             ! copy into bank 0
+	LSLGT   D1Re0,D1Re0,D1Ar3       ! ( > 0) ? left shift B
+	LSLGT   D0Ar6,D0Ar6,D0Ar2       ! ( > 0) ? left shift curbit
+!!
+!! Now we start the divide proper, logic is
+!!
+!!       if ( A >= B ) add curbit to result and subtract B from A
+!!       shift curbit and B down by 1 in either case
+!!
+$LIDMCLoop:
+	CMP     D1Ar1, D1Re0            ! ( A >= B )?
+	ADDCC   D0Re0, D0Re0, D0Ar6     ! If yes result += curbit
+	SUBCC   D1Ar1, D1Ar1, D1Re0     ! and A -= B
+	LSRS    D0Ar6, D0Ar6, #1        ! Shift down curbit, is it zero?
+	LSR     D1Re0, D1Re0, #1        ! Shift down B
+	BNZ     $LIDMCLoop               ! Was single bit in curbit lost?
+	ORS     D0Ar4,D0Ar4,D0Ar4       ! Return neg result?
+	NEG     D0Ar2,D0Re0             ! Calulate neg result
+	MOVMI   D0Re0,D0Ar2             ! Yes: Take neg result
+	MOV     PC,D1RtP
+	.size   ___divsi3,.-___divsi3
diff --git a/arch/metag/lib/ip_fast_csum.S b/arch/metag/lib/ip_fast_csum.S
new file mode 100644
index 0000000..533b1e7
--- /dev/null
+++ b/arch/metag/lib/ip_fast_csum.S
@@ -0,0 +1,32 @@
+
+	.text
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ *
+ * extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl);
+ *
+ */
+	.global _ip_fast_csum
+	.type   _ip_fast_csum,function
+_ip_fast_csum:
+	!! TXRPT needs loops - 1
+	SUBS	TXRPT,D0Ar2,#1
+	MOV	D0Re0,#0
+	BLO	$Lfast_csum_exit
+$Lfast_csum_loop:
+	GETD	D1Ar3,[D1Ar1++]
+	ADDS	D0Re0,D0Re0,D1Ar3
+	ADDCS   D0Re0,D0Re0,#1
+	BR	$Lfast_csum_loop
+	LSR	D0Ar4,D0Re0,#16
+	AND	D0Re0,D0Re0,#0xffff
+	AND	D0Ar4,D0Ar4,#0xffff
+	ADD	D0Re0,D0Re0,D0Ar4
+	LSR	D0Ar4,D0Re0,#16
+	ADD	D0Re0,D0Re0,D0Ar4
+	XOR	D0Re0,D0Re0,#-1
+	AND	D0Re0,D0Re0,#0xffff
+$Lfast_csum_exit:
+	MOV	PC,D1RtP
+	.size _ip_fast_csum,.-_ip_fast_csum
diff --git a/arch/metag/lib/lshrdi3.S b/arch/metag/lib/lshrdi3.S
new file mode 100644
index 0000000..47f7202
--- /dev/null
+++ b/arch/metag/lib/lshrdi3.S
@@ -0,0 +1,33 @@
+! Copyright (C) 2012 by Imagination Technologies Ltd.
+!
+! 64-bit logical shift right routine.
+!
+
+	.text
+	.global ___lshrdi3
+	.type   ___lshrdi3,function
+
+___lshrdi3:
+	MOV     D0Re0,D0Ar2
+	MOV     D1Re0,D1Ar1
+	CMP     D1Ar3,#0                ! COUNT == 0
+	MOVEQ   PC,D1RtP                ! Yes, return
+
+	MOV     D0Ar4,D1Ar3
+	SUBS    D1Ar3,D1Ar3,#32         ! N = COUNT - 32
+	BGE     $L30
+
+!! Shift < 32
+	NEG     D1Ar3,D1Ar3             ! N = - N
+	LSR     D0Re0,D0Re0,D0Ar4       ! LO = LO >> COUNT
+	LSL     D0Ar6,D1Re0,D1Ar3       ! TMP= HI << -(COUNT - 32)
+	OR      D0Re0,D0Re0,D0Ar6       ! LO = LO | TMP
+	SWAP    D1Ar3,D0Ar4
+	LSR     D1Re0,D1Re0,D1Ar3       ! HI = HI >> COUNT
+	MOV     PC,D1RtP
+$L30:
+!! Shift >= 32
+	LSR     D0Re0,D1Re0,D1Ar3       ! LO = HI >> N
+	MOV     D1Re0,#0                ! HI = 0
+	MOV     PC,D1RtP
+	.size ___lshrdi3,.-___lshrdi3
diff --git a/arch/metag/lib/memcpy.S b/arch/metag/lib/memcpy.S
new file mode 100644
index 0000000..46b7a2b
--- /dev/null
+++ b/arch/metag/lib/memcpy.S
@@ -0,0 +1,185 @@
+!   Copyright (C) 2008-2012 Imagination Technologies Ltd.
+
+	.text
+	.global	_memcpy
+	.type	_memcpy,function
+! D1Ar1 dst
+! D0Ar2 src
+! D1Ar3 cnt
+! D0Re0 dst
+_memcpy:
+	CMP 	D1Ar3, #16
+	MOV 	A1.2, D0Ar2		! source pointer
+	MOV 	A0.2, D1Ar1		! destination pointer
+	MOV 	A0.3, D1Ar1		! for return value
+! If there are less than 16 bytes to copy use the byte copy loop
+	BGE 	$Llong_copy
+
+$Lbyte_copy:
+! Simply copy a byte at a time
+	SUBS	TXRPT, D1Ar3, #1
+	BLT	$Lend
+$Lloop_byte:
+	GETB 	D1Re0, [A1.2++]
+	SETB 	[A0.2++], D1Re0
+	BR	$Lloop_byte
+
+$Lend:
+! Finally set return value and return
+	MOV 	D0Re0, A0.3
+	MOV 	PC, D1RtP
+
+$Llong_copy:
+	ANDS 	D1Ar5, D1Ar1, #7	! test destination alignment
+	BZ	$Laligned_dst
+
+! The destination address is not 8 byte aligned. We will copy bytes from
+! the source to the destination until the remaining data has an 8 byte
+! destination address alignment (i.e we should never copy more than 7
+! bytes here).
+$Lalign_dst:
+	GETB 	D0Re0, [A1.2++]
+	ADD 	D1Ar5, D1Ar5, #1	! dest is aligned when D1Ar5 reaches #8
+	SUB 	D1Ar3, D1Ar3, #1	! decrement count of remaining bytes
+	SETB 	[A0.2++], D0Re0
+	CMP 	D1Ar5, #8
+	BNE 	$Lalign_dst
+
+! We have at least (16 - 7) = 9 bytes to copy - calculate the number of 8 byte
+! blocks, then jump to the unaligned copy loop or fall through to the aligned
+! copy loop as appropriate.
+$Laligned_dst:
+	MOV	D0Ar4, A1.2
+	LSR 	D1Ar5, D1Ar3, #3	! D1Ar5 = number of 8 byte blocks
+	ANDS 	D0Ar4, D0Ar4, #7	! test source alignment
+	BNZ 	$Lunaligned_copy	! if unaligned, use unaligned copy loop
+
+! Both source and destination are 8 byte aligned - the easy case.
+$Laligned_copy:
+	LSRS	D1Ar5, D1Ar3, #5	! D1Ar5 = number of 32 byte blocks
+	BZ	$Lbyte_copy
+	SUB	TXRPT, D1Ar5, #1
+
+$Laligned_32:
+	GETL 	D0Re0, D1Re0, [A1.2++]
+	GETL 	D0Ar6, D1Ar5, [A1.2++]
+	SETL 	[A0.2++], D0Re0, D1Re0
+	SETL 	[A0.2++], D0Ar6, D1Ar5
+	GETL 	D0Re0, D1Re0, [A1.2++]
+	GETL 	D0Ar6, D1Ar5, [A1.2++]
+	SETL 	[A0.2++], D0Re0, D1Re0
+	SETL 	[A0.2++], D0Ar6, D1Ar5
+	BR	$Laligned_32
+
+! If there are any remaining bytes use the byte copy loop, otherwise we are done
+	ANDS 	D1Ar3, D1Ar3, #0x1f
+	BNZ	$Lbyte_copy
+	B	$Lend
+
+! The destination is 8 byte aligned but the source is not, and there are 8
+! or more bytes to be copied.
+$Lunaligned_copy:
+! Adjust the source pointer (A1.2) to the 8 byte boundary before its
+! current value
+	MOV 	D0Ar4, A1.2
+	MOV 	D0Ar6, A1.2
+	ANDMB 	D0Ar4, D0Ar4, #0xfff8
+	MOV 	A1.2, D0Ar4
+! Save the number of bytes of mis-alignment in D0Ar4 for use later
+	SUBS 	D0Ar6, D0Ar6, D0Ar4
+	MOV	D0Ar4, D0Ar6
+! if there is no mis-alignment after all, use the aligned copy loop
+	BZ 	$Laligned_copy
+
+! prefetch 8 bytes
+	GETL 	D0Re0, D1Re0, [A1.2]
+
+	SUB	TXRPT, D1Ar5, #1
+
+! There are 3 mis-alignment cases to be considered. Less than 4 bytes, exactly
+! 4 bytes, and more than 4 bytes.
+	CMP 	D0Ar6, #4
+	BLT 	$Lunaligned_1_2_3	! use 1-3 byte mis-alignment loop
+	BZ 	$Lunaligned_4		! use 4 byte mis-alignment loop
+
+! The mis-alignment is more than 4 bytes
+$Lunaligned_5_6_7:
+	SUB 	D0Ar6, D0Ar6, #4
+! Calculate the bit offsets required for the shift operations necesssary
+! to align the data.
+! D0Ar6 = bit offset, D1Ar5 = (32 - bit offset)
+	MULW 	D0Ar6, D0Ar6, #8
+	MOV	D1Ar5, #32
+	SUB	D1Ar5, D1Ar5, D0Ar6
+! Move data 4 bytes before we enter the main loop
+	MOV 	D0Re0, D1Re0
+
+$Lloop_5_6_7:
+	GETL 	D0Ar2, D1Ar1, [++A1.2]
+! form 64-bit data in D0Re0, D1Re0
+	LSR 	D0Re0, D0Re0, D0Ar6
+	MOV 	D1Re0, D0Ar2
+	LSL 	D1Re0, D1Re0, D1Ar5
+	ADD 	D0Re0, D0Re0, D1Re0
+
+	LSR 	D0Ar2, D0Ar2, D0Ar6
+	LSL 	D1Re0, D1Ar1, D1Ar5
+	ADD 	D1Re0, D1Re0, D0Ar2
+
+	SETL 	[A0.2++], D0Re0, D1Re0
+	MOV 	D0Re0, D1Ar1
+	BR	$Lloop_5_6_7
+
+	B 	$Lunaligned_end
+
+$Lunaligned_1_2_3:
+! Calculate the bit offsets required for the shift operations necesssary
+! to align the data.
+! D0Ar6 = bit offset, D1Ar5 = (32 - bit offset)
+	MULW 	D0Ar6, D0Ar6, #8
+	MOV	D1Ar5, #32
+	SUB	D1Ar5, D1Ar5, D0Ar6
+
+$Lloop_1_2_3:
+! form 64-bit data in D0Re0,D1Re0
+	LSR 	D0Re0, D0Re0, D0Ar6
+	LSL 	D1Ar1, D1Re0, D1Ar5
+	ADD 	D0Re0, D0Re0, D1Ar1
+	MOV	D0Ar2, D1Re0
+	LSR 	D0FrT, D0Ar2, D0Ar6
+	GETL 	D0Ar2, D1Ar1, [++A1.2]
+
+	MOV 	D1Re0, D0Ar2
+	LSL 	D1Re0, D1Re0, D1Ar5
+	ADD 	D1Re0, D1Re0, D0FrT
+
+	SETL 	[A0.2++], D0Re0, D1Re0
+	MOV 	D0Re0, D0Ar2
+	MOV 	D1Re0, D1Ar1
+	BR	$Lloop_1_2_3
+
+	B 	$Lunaligned_end
+
+! The 4 byte mis-alignment case - this does not require any shifting, just a
+! shuffling of registers.
+$Lunaligned_4:
+	MOV 	D0Re0, D1Re0
+$Lloop_4:
+	GETL 	D0Ar2, D1Ar1, [++A1.2]
+	MOV 	D1Re0, D0Ar2
+	SETL 	[A0.2++], D0Re0, D1Re0
+	MOV 	D0Re0, D1Ar1
+	BR	$Lloop_4
+
+$Lunaligned_end:
+! If there are no remaining bytes to copy, we are done.
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ	$Lend
+! Re-adjust the source pointer (A1.2) back to the actual (unaligned) byte
+! address of the remaining bytes, and fall through to the byte copy loop.
+	MOV 	D0Ar6, A1.2
+	ADD 	D1Ar5, D0Ar4, D0Ar6
+	MOV 	A1.2, D1Ar5
+	B	$Lbyte_copy
+
+	.size _memcpy,.-_memcpy
diff --git a/arch/metag/lib/memmove.S b/arch/metag/lib/memmove.S
new file mode 100644
index 0000000..228ea04
--- /dev/null
+++ b/arch/metag/lib/memmove.S
@@ -0,0 +1,345 @@
+!   Copyright (C) 2008-2012 Imagination Technologies Ltd.
+
+	.text
+	.global	_memmove
+	.type	_memmove,function
+! D1Ar1 dst
+! D0Ar2 src
+! D1Ar3 cnt
+! D0Re0 dst
+_memmove:
+	CMP 	D1Ar3, #0
+	MOV 	D0Re0, D1Ar1
+	BZ 	$LEND2
+	MSETL 	[A0StP], D0.5, D0.6, D0.7
+	MOV 	D1Ar5, D0Ar2
+	CMP 	D1Ar1, D1Ar5
+	BLT 	$Lforwards_copy
+	SUB 	D0Ar4, D1Ar1, D1Ar3
+	ADD 	D0Ar4, D0Ar4, #1
+	CMP 	D0Ar2, D0Ar4
+	BLT 	$Lforwards_copy
+	! should copy backwards
+	MOV 	D1Re0, D0Ar2
+	! adjust pointer to the end of mem
+	ADD 	D0Ar2, D1Re0, D1Ar3
+	ADD 	D1Ar1, D1Ar1, D1Ar3
+
+	MOV 	A1.2, D0Ar2
+	MOV 	A0.2, D1Ar1
+	CMP 	D1Ar3, #8
+	BLT 	$Lbbyte_loop
+
+	MOV 	D0Ar4, D0Ar2
+	MOV 	D1Ar5, D1Ar1
+
+	! test 8 byte alignment
+	ANDS 	D1Ar5, D1Ar5, #7
+	BNE 	$Lbdest_unaligned
+
+	ANDS 	D0Ar4, D0Ar4, #7
+	BNE 	$Lbsrc_unaligned
+
+	LSR 	D1Ar5, D1Ar3, #3
+
+$Lbaligned_loop:
+	GETL 	D0Re0, D1Re0, [--A1.2]
+	SETL 	[--A0.2], D0Re0, D1Re0
+	SUBS 	D1Ar5, D1Ar5, #1
+	BNE 	$Lbaligned_loop
+
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ 	$Lbbyte_loop_exit
+$Lbbyte_loop:
+	GETB 	D1Re0, [--A1.2]
+	SETB 	[--A0.2], D1Re0
+	SUBS 	D1Ar3, D1Ar3, #1
+	BNE 	$Lbbyte_loop
+$Lbbyte_loop_exit:
+	MOV 	D0Re0, A0.2
+$LEND:
+	SUB 	A0.2, A0StP, #24
+	MGETL 	D0.5, D0.6, D0.7, [A0.2]
+	SUB 	A0StP, A0StP, #24
+$LEND2:
+	MOV 	PC, D1RtP
+
+$Lbdest_unaligned:
+	GETB 	D0Re0, [--A1.2]
+	SETB 	[--A0.2], D0Re0
+	SUBS 	D1Ar5, D1Ar5, #1
+	SUB 	D1Ar3, D1Ar3, #1
+	BNE 	$Lbdest_unaligned
+	CMP 	D1Ar3, #8
+	BLT 	$Lbbyte_loop
+$Lbsrc_unaligned:
+	LSR 	D1Ar5, D1Ar3, #3
+	! adjust A1.2
+	MOV 	D0Ar4, A1.2
+	! save original address
+	MOV 	D0Ar6, A1.2
+
+	ADD 	D0Ar4, D0Ar4, #7
+	ANDMB 	D0Ar4, D0Ar4, #0xfff8
+	! new address is the 8-byte aligned one above the original
+	MOV 	A1.2, D0Ar4
+
+	! A0.2 dst 64-bit is aligned
+	! measure the gap size
+	SUB 	D0Ar6, D0Ar4, D0Ar6
+	MOVS 	D0Ar4, D0Ar6
+	! keep this information for the later adjustment
+	! both aligned
+	BZ 	$Lbaligned_loop
+
+	! prefetch
+	GETL 	D0Re0, D1Re0, [--A1.2]
+
+	CMP 	D0Ar6, #4
+	BLT 	$Lbunaligned_1_2_3
+	! 32-bit aligned
+	BZ 	$Lbaligned_4
+
+	SUB 	D0Ar6, D0Ar6, #4
+	! D1.6 stores the gap size in bits
+	MULW 	D1.6, D0Ar6, #8
+	MOV 	D0.6, #32
+	! D0.6 stores the complement of the gap size
+	SUB 	D0.6, D0.6, D1.6
+
+$Lbunaligned_5_6_7:
+	GETL 	D0.7, D1.7, [--A1.2]
+	! form 64-bit data in D0Re0, D1Re0
+	MOV 	D1Re0, D0Re0
+	! D1Re0 << gap-size
+	LSL 	D1Re0, D1Re0, D1.6
+	MOV 	D0Re0, D1.7
+	! D0Re0 >> complement
+	LSR 	D0Re0, D0Re0, D0.6
+	MOV 	D1.5, D0Re0
+	! combine the both
+	ADD 	D1Re0, D1Re0, D1.5
+
+	MOV 	D1.5, D1.7
+	LSL 	D1.5, D1.5, D1.6
+	MOV 	D0Re0, D0.7
+	LSR 	D0Re0, D0Re0, D0.6
+	MOV 	D0.5, D1.5
+	ADD 	D0Re0, D0Re0, D0.5
+
+	SETL 	[--A0.2], D0Re0, D1Re0
+	MOV 	D0Re0, D0.7
+	MOV 	D1Re0, D1.7
+	SUBS 	D1Ar5, D1Ar5, #1
+	BNE 	$Lbunaligned_5_6_7
+
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ 	$Lbbyte_loop_exit
+	! Adjust A1.2
+	! A1.2 <- A1.2 +8 - gapsize
+	ADD 	A1.2, A1.2, #8
+	SUB 	A1.2, A1.2, D0Ar4
+	B 	$Lbbyte_loop
+
+$Lbunaligned_1_2_3:
+	MULW 	D1.6, D0Ar6, #8
+	MOV 	D0.6, #32
+	SUB 	D0.6, D0.6, D1.6
+
+$Lbunaligned_1_2_3_loop:
+	GETL 	D0.7, D1.7, [--A1.2]
+	! form 64-bit data in D0Re0, D1Re0
+	LSL 	D1Re0, D1Re0, D1.6
+	! save D0Re0 for later use
+	MOV 	D0.5, D0Re0
+	LSR 	D0Re0, D0Re0, D0.6
+	MOV 	D1.5, D0Re0
+	ADD 	D1Re0, D1Re0, D1.5
+
+	! orignal data in D0Re0
+	MOV 	D1.5, D0.5
+	LSL 	D1.5, D1.5, D1.6
+	MOV 	D0Re0, D1.7
+	LSR 	D0Re0, D0Re0, D0.6
+	MOV 	D0.5, D1.5
+	ADD 	D0Re0, D0Re0, D0.5
+
+	SETL 	[--A0.2], D0Re0, D1Re0
+	MOV 	D0Re0, D0.7
+	MOV 	D1Re0, D1.7
+	SUBS 	D1Ar5, D1Ar5, #1
+	BNE 	$Lbunaligned_1_2_3_loop
+
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ 	$Lbbyte_loop_exit
+	! Adjust A1.2
+	ADD 	A1.2, A1.2, #8
+	SUB 	A1.2, A1.2, D0Ar4
+	B 	$Lbbyte_loop
+
+$Lbaligned_4:
+	GETL 	D0.7, D1.7, [--A1.2]
+	MOV 	D1Re0, D0Re0
+	MOV 	D0Re0, D1.7
+	SETL 	[--A0.2], D0Re0, D1Re0
+	MOV 	D0Re0, D0.7
+	MOV 	D1Re0, D1.7
+	SUBS 	D1Ar5, D1Ar5, #1
+	BNE 	$Lbaligned_4
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ 	$Lbbyte_loop_exit
+	! Adjust A1.2
+	ADD 	A1.2, A1.2, #8
+	SUB 	A1.2, A1.2, D0Ar4
+	B 	$Lbbyte_loop
+
+$Lforwards_copy:
+	MOV 	A1.2, D0Ar2
+	MOV 	A0.2, D1Ar1
+	CMP 	D1Ar3, #8
+	BLT 	$Lfbyte_loop
+
+	MOV 	D0Ar4, D0Ar2
+	MOV 	D1Ar5, D1Ar1
+
+	ANDS 	D1Ar5, D1Ar5, #7
+	BNE 	$Lfdest_unaligned
+
+	ANDS 	D0Ar4, D0Ar4, #7
+	BNE 	$Lfsrc_unaligned
+
+	LSR 	D1Ar5, D1Ar3, #3
+
+$Lfaligned_loop:
+	GETL 	D0Re0, D1Re0, [A1.2++]
+	SUBS 	D1Ar5, D1Ar5, #1
+	SETL 	[A0.2++], D0Re0, D1Re0
+	BNE 	$Lfaligned_loop
+
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ 	$Lfbyte_loop_exit
+$Lfbyte_loop:
+	GETB 	D1Re0, [A1.2++]
+	SETB 	[A0.2++], D1Re0
+	SUBS 	D1Ar3, D1Ar3, #1
+	BNE 	$Lfbyte_loop
+$Lfbyte_loop_exit:
+	MOV 	D0Re0, D1Ar1
+	B 	$LEND
+
+$Lfdest_unaligned:
+	GETB 	D0Re0, [A1.2++]
+	ADD 	D1Ar5, D1Ar5, #1
+	SUB 	D1Ar3, D1Ar3, #1
+	SETB 	[A0.2++], D0Re0
+	CMP 	D1Ar5, #8
+	BNE 	$Lfdest_unaligned
+	CMP 	D1Ar3, #8
+	BLT 	$Lfbyte_loop
+$Lfsrc_unaligned:
+	! adjust A1.2
+	LSR 	D1Ar5, D1Ar3, #3
+
+	MOV 	D0Ar4, A1.2
+	MOV 	D0Ar6, A1.2
+	ANDMB 	D0Ar4, D0Ar4, #0xfff8
+	MOV 	A1.2, D0Ar4
+
+	! A0.2 dst 64-bit is aligned
+	SUB 	D0Ar6, D0Ar6, D0Ar4
+	! keep the information for the later adjustment
+	MOVS 	D0Ar4, D0Ar6
+
+	! both aligned
+	BZ 	$Lfaligned_loop
+
+	! prefetch
+	GETL 	D0Re0, D1Re0, [A1.2]
+
+	CMP 	D0Ar6, #4
+	BLT 	$Lfunaligned_1_2_3
+	BZ 	$Lfaligned_4
+
+	SUB 	D0Ar6, D0Ar6, #4
+	MULW 	D0.6, D0Ar6, #8
+	MOV 	D1.6, #32
+	SUB 	D1.6, D1.6, D0.6
+
+$Lfunaligned_5_6_7:
+	GETL 	D0.7, D1.7, [++A1.2]
+	! form 64-bit data in D0Re0, D1Re0
+	MOV 	D0Re0, D1Re0
+	LSR 	D0Re0, D0Re0, D0.6
+	MOV 	D1Re0, D0.7
+	LSL 	D1Re0, D1Re0, D1.6
+	MOV 	D0.5, D1Re0
+	ADD 	D0Re0, D0Re0, D0.5
+
+	MOV 	D0.5, D0.7
+	LSR 	D0.5, D0.5, D0.6
+	MOV 	D1Re0, D1.7
+	LSL 	D1Re0, D1Re0, D1.6
+	MOV 	D1.5, D0.5
+	ADD 	D1Re0, D1Re0, D1.5
+
+	SETL 	[A0.2++], D0Re0, D1Re0
+	MOV 	D0Re0, D0.7
+	MOV 	D1Re0, D1.7
+	SUBS 	D1Ar5, D1Ar5, #1
+	BNE 	$Lfunaligned_5_6_7
+
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ 	$Lfbyte_loop_exit
+	! Adjust A1.2
+	ADD	A1.2, A1.2, D0Ar4
+	B 	$Lfbyte_loop
+
+$Lfunaligned_1_2_3:
+	MULW 	D0.6, D0Ar6, #8
+	MOV 	D1.6, #32
+	SUB 	D1.6, D1.6, D0.6
+
+$Lfunaligned_1_2_3_loop:
+	GETL 	D0.7, D1.7, [++A1.2]
+	! form 64-bit data in D0Re0, D1Re0
+	LSR 	D0Re0, D0Re0, D0.6
+	MOV 	D1.5, D1Re0
+	LSL 	D1Re0, D1Re0, D1.6
+	MOV 	D0.5, D1Re0
+	ADD 	D0Re0, D0Re0, D0.5
+
+	MOV 	D0.5, D1.5
+	LSR 	D0.5, D0.5, D0.6
+	MOV 	D1Re0, D0.7
+	LSL 	D1Re0, D1Re0, D1.6
+	MOV 	D1.5, D0.5
+	ADD 	D1Re0, D1Re0, D1.5
+
+	SETL 	[A0.2++], D0Re0, D1Re0
+	MOV 	D0Re0, D0.7
+	MOV 	D1Re0, D1.7
+	SUBS 	D1Ar5, D1Ar5, #1
+	BNE 	$Lfunaligned_1_2_3_loop
+
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ 	$Lfbyte_loop_exit
+	! Adjust A1.2
+	ADD	A1.2, A1.2, D0Ar4
+	B 	$Lfbyte_loop
+
+$Lfaligned_4:
+	GETL 	D0.7, D1.7, [++A1.2]
+	MOV 	D0Re0, D1Re0
+	MOV 	D1Re0, D0.7
+	SETL 	[A0.2++], D0Re0, D1Re0
+	MOV 	D0Re0, D0.7
+	MOV 	D1Re0, D1.7
+	SUBS 	D1Ar5, D1Ar5, #1
+	BNE 	$Lfaligned_4
+	ANDS 	D1Ar3, D1Ar3, #7
+	BZ 	$Lfbyte_loop_exit
+	! Adjust A1.2
+	ADD	A1.2, A1.2, D0Ar4
+	B 	$Lfbyte_loop
+
+	.size _memmove,.-_memmove
diff --git a/arch/metag/lib/memset.S b/arch/metag/lib/memset.S
new file mode 100644
index 0000000..721085b
--- /dev/null
+++ b/arch/metag/lib/memset.S
@@ -0,0 +1,86 @@
+!   Copyright (C) 2008-2012 Imagination Technologies Ltd.
+
+	.text
+	.global	_memset
+	.type	_memset,function
+! D1Ar1 dst
+! D0Ar2 c
+! D1Ar3 cnt
+! D0Re0 dst
+_memset:
+	AND	D0Ar2,D0Ar2,#0xFF	! Ensure a byte input value
+	MULW 	D0Ar2,D0Ar2,#0x0101	! Duplicate byte value into  0-15
+	ANDS	D0Ar4,D1Ar1,#7		! Extract bottom LSBs of dst
+	LSL 	D0Re0,D0Ar2,#16		! Duplicate byte value into 16-31
+	ADD	A0.2,D0Ar2,D0Re0	! Duplicate byte value into 4 (A0.2)
+	MOV	D0Re0,D1Ar1		! Return dst
+	BZ	$LLongStub		! if start address is aligned
+	! start address is not aligned on an 8 byte boundary, so we
+	! need the number of bytes up to the next 8 byte address
+	! boundary, or the length of the string if less than 8, in D1Ar5
+	MOV	D0Ar2,#8		! Need 8 - N in D1Ar5 ...
+	SUB	D1Ar5,D0Ar2,D0Ar4	!            ... subtract N
+	CMP	D1Ar3,D1Ar5
+	MOVMI	D1Ar5,D1Ar3
+	B	$LByteStub		! dst is mis-aligned, do $LByteStub
+
+!
+! Preamble to LongLoop which generates 4*8 bytes per interation (5 cycles)
+!
+$LLongStub:
+	LSRS	D0Ar2,D1Ar3,#5
+	AND	D1Ar3,D1Ar3,#0x1F
+	MOV	A1.2,A0.2
+	BEQ	$LLongishStub
+	SUB	TXRPT,D0Ar2,#1
+	CMP	D1Ar3,#0
+$LLongLoop:
+	SETL 	[D1Ar1++],A0.2,A1.2
+	SETL 	[D1Ar1++],A0.2,A1.2
+	SETL 	[D1Ar1++],A0.2,A1.2
+	SETL 	[D1Ar1++],A0.2,A1.2
+	BR	$LLongLoop
+	BZ	$Lexit
+!
+! Preamble to LongishLoop which generates 1*8 bytes per interation (2 cycles)
+!
+$LLongishStub:
+	LSRS	D0Ar2,D1Ar3,#3
+	AND	D1Ar3,D1Ar3,#0x7
+	MOV	D1Ar5,D1Ar3
+	BEQ	$LByteStub
+	SUB	TXRPT,D0Ar2,#1
+	CMP	D1Ar3,#0
+$LLongishLoop:
+	SETL 	[D1Ar1++],A0.2,A1.2
+	BR	$LLongishLoop
+	BZ	$Lexit
+!
+! This does a byte structured burst of up to 7 bytes
+!
+!	D1Ar1 should point to the location required
+!	D1Ar3 should be the remaining total byte count
+!	D1Ar5 should be burst size (<= D1Ar3)
+!
+$LByteStub:
+	SUBS	D1Ar3,D1Ar3,D1Ar5	! Reduce count
+	ADD	D1Ar1,D1Ar1,D1Ar5	! Advance pointer to end of area
+	MULW	D1Ar5,D1Ar5,#4		! Scale to (1*4), (2*4), (3*4)
+	SUB	D1Ar5,D1Ar5,#(8*4)	! Rebase to -(7*4), -(6*4), -(5*4), ...
+	MOV	A1.2,D1Ar5
+	SUB	PC,CPC1,A1.2		! Jump into table below
+	SETB 	[D1Ar1+#(-7)],A0.2
+	SETB 	[D1Ar1+#(-6)],A0.2
+	SETB 	[D1Ar1+#(-5)],A0.2
+	SETB 	[D1Ar1+#(-4)],A0.2
+	SETB 	[D1Ar1+#(-3)],A0.2
+	SETB 	[D1Ar1+#(-2)],A0.2
+	SETB 	[D1Ar1+#(-1)],A0.2
+!
+! Return if all data has been output, otherwise do $LLongStub
+!
+	BNZ	$LLongStub
+$Lexit:
+	MOV	PC,D1RtP
+        .size _memset,.-_memset
+
diff --git a/arch/metag/lib/modsi3.S b/arch/metag/lib/modsi3.S
new file mode 100644
index 0000000..210cfa8
--- /dev/null
+++ b/arch/metag/lib/modsi3.S
@@ -0,0 +1,38 @@
+! Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
+!               Imagination Technologies Ltd
+!
+! Integer modulus routines.
+!
+!!
+!! 32-bit modulus unsigned i/p - passed unsigned 32-bit numbers
+!!
+	.text
+	.global ___umodsi3
+	.type   ___umodsi3,function
+	.align  2
+___umodsi3:
+	MOV     D0FrT,D1RtP             ! Save original return address
+	CALLR   D1RtP,___udivsi3
+	MOV     D1RtP,D0FrT             ! Recover return address
+	MOV     D0Re0,D1Ar1             ! Return remainder
+	MOV     PC,D1RtP
+	.size   ___umodsi3,.-___umodsi3
+
+!!
+!! 32-bit modulus signed i/p - passed signed 32-bit numbers
+!!
+	.global ___modsi3
+	.type   ___modsi3,function
+	.align  2
+___modsi3:
+	MOV     D0FrT,D1RtP             ! Save original return address
+	MOV     A0.2,D1Ar1              ! Save A in A0.2
+	CALLR   D1RtP,___divsi3
+	MOV     D1RtP,D0FrT             ! Recover return address
+	MOV     D1Re0,A0.2              ! Recover A
+	MOV     D0Re0,D1Ar1             ! Return remainder
+	ORS     D1Re0,D1Re0,D1Re0       ! Was A negative?
+	NEG     D1Ar1,D1Ar1             ! Negate remainder
+	MOVMI   D0Re0,D1Ar1             ! Return neg remainder
+	MOV     PC, D1RtP
+	.size   ___modsi3,.-___modsi3
diff --git a/arch/metag/lib/muldi3.S b/arch/metag/lib/muldi3.S
new file mode 100644
index 0000000..ee66ca8
--- /dev/null
+++ b/arch/metag/lib/muldi3.S
@@ -0,0 +1,44 @@
+! Copyright (C) 2012 by Imagination Technologies Ltd.
+!
+! 64-bit multiply routine.
+!
+
+!
+! 64-bit signed/unsigned multiply
+!
+! A = D1Ar1:D0Ar2 = a 2^48 + b 2^32 +  c 2^16 + d 2^0
+!
+! B = D1Ar3:D0Ar4 = w 2^48 + x 2^32 +  y 2^16 + z 2^0
+!
+	.text
+	.global ___muldi3
+	.type   ___muldi3,function
+
+___muldi3:
+	MULD    D1Re0,D1Ar1,D0Ar4       ! (a 2^48 + b 2^32)(y 2^16 + z 2^0)
+	MULD    D0Re0,D0Ar2,D1Ar3       ! (w 2^48 + x 2^32)(c 2^16 + d 2^0)
+	ADD     D1Re0,D1Re0,D0Re0
+
+	MULW    D0Re0,D0Ar2,D0Ar4       ! (d 2^0)  * (z 2^0)
+
+	RTDW    D0Ar2,D0Ar2
+	MULW    D0Ar6,D0Ar2,D0Ar4       ! (c 2^16)(z 2^0)
+	LSR     D1Ar5,D0Ar6,#16
+	LSL     D0Ar6,D0Ar6,#16
+	ADDS    D0Re0,D0Re0,D0Ar6
+	ADDCS   D1Re0,D1Re0,#1
+	RTDW    D0Ar4,D0Ar4
+	ADD     D1Re0,D1Re0,D1Ar5
+
+	MULW    D0Ar6,D0Ar2,D0Ar4       ! (c 2^16)(y 2^16)
+	ADD     D1Re0,D1Re0,D0Ar6
+
+	RTDW    D0Ar2,D0Ar2
+	MULW    D0Ar6,D0Ar2,D0Ar4       ! (d 2^0)(y 2^16)
+	LSR     D1Ar5,D0Ar6,#16
+	LSL     D0Ar6,D0Ar6,#16
+	ADDS    D0Re0,D0Re0,D0Ar6
+	ADD     D1Re0,D1Re0,D1Ar5
+	ADDCS   D1Re0,D1Re0,#1
+	MOV     PC, D1RtP
+	.size ___muldi3,.-___muldi3
diff --git a/arch/metag/lib/usercopy.c b/arch/metag/lib/usercopy.c
new file mode 100644
index 0000000..72a4c6d
--- /dev/null
+++ b/arch/metag/lib/usercopy.c
@@ -0,0 +1,1341 @@
+/*
+ * User address space access functions.
+ * The non-inlined parts of asm-metag/uaccess.h are here.
+ *
+ * Copyright (C) 2006, Imagination Technologies.
+ * Copyright (C) 2000, Axis Communications AB.
+ *
+ * Written by Hans-Peter Nilsson.
+ * Pieces used from memcpy, originally by Kenny Ranerup long time ago.
+ * Modified for Meta by Will Newton.
+ */
+
+#include <linux/uaccess.h>
+#include <asm/cache.h>			/* def of L1_CACHE_BYTES */
+
+#define USE_RAPF
+#define RAPF_MIN_BUF_SIZE	(3*L1_CACHE_BYTES)
+
+
+/* The "double write" in this code is because the Meta will not fault
+ * immediately unless the memory pipe is forced to by e.g. a data stall or
+ * another memory op. The second write should be discarded by the write
+ * combiner so should have virtually no cost.
+ */
+
+#define __asm_copy_user_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm__ __volatile__ (				\
+			COPY				\
+		"1:\n"					\
+		"	.section .fixup,\"ax\"\n"	\
+		"	MOV D1Ar1,#0\n"			\
+			FIXUP				\
+		"	MOVT    D1Ar1,#HI(1b)\n"	\
+		"	JUMP    D1Ar1,#LO(1b)\n"	\
+		"	.previous\n"			\
+		"	.section __ex_table,\"a\"\n"	\
+			TENTRY				\
+		"	.previous\n"			\
+		: "=r" (to), "=r" (from), "=r" (ret)	\
+		: "0" (to), "1" (from), "2" (ret)	\
+		: "D1Ar1", "memory")
+
+
+#define __asm_copy_to_user_1(to, from, ret)	\
+	__asm_copy_user_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"	\
+		"	SETB [%0],D1Ar1\n"	\
+		"2:	SETB [%0++],D1Ar1\n",	\
+		"3:	ADD  %2,%2,#1\n",	\
+		"	.long 2b,3b\n")
+
+#define __asm_copy_to_user_2x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_user_cont(to, from, ret,		\
+		"	GETW D1Ar1,[%1++]\n"		\
+		"	SETW [%0],D1Ar1\n"		\
+		"2:	SETW [%0++],D1Ar1\n" COPY,	\
+		"3:	ADD  %2,%2,#2\n" FIXUP,		\
+		"	.long 2b,3b\n" TENTRY)
+
+#define __asm_copy_to_user_2(to, from, ret) \
+	__asm_copy_to_user_2x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_to_user_3(to, from, ret) \
+	__asm_copy_to_user_2x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"	SETB [%0],D1Ar1\n"		\
+		"4:	SETB [%0++],D1Ar1\n",		\
+		"5:	ADD  %2,%2,#1\n",		\
+		"	.long 4b,5b\n")
+
+#define __asm_copy_to_user_4x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_user_cont(to, from, ret,		\
+		"	GETD D1Ar1,[%1++]\n"		\
+		"	SETD [%0],D1Ar1\n"		\
+		"2:	SETD [%0++],D1Ar1\n" COPY,	\
+		"3:	ADD  %2,%2,#4\n" FIXUP,		\
+		"	.long 2b,3b\n" TENTRY)
+
+#define __asm_copy_to_user_4(to, from, ret) \
+	__asm_copy_to_user_4x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_to_user_5(to, from, ret) \
+	__asm_copy_to_user_4x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"	SETB [%0],D1Ar1\n"		\
+		"4:	SETB [%0++],D1Ar1\n",		\
+		"5:	ADD  %2,%2,#1\n",		\
+		"	.long 4b,5b\n")
+
+#define __asm_copy_to_user_6x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_to_user_4x_cont(to, from, ret,	\
+		"	GETW D1Ar1,[%1++]\n"		\
+		"	SETW [%0],D1Ar1\n"		\
+		"4:	SETW [%0++],D1Ar1\n" COPY,	\
+		"5:	ADD  %2,%2,#2\n" FIXUP,		\
+		"	.long 4b,5b\n" TENTRY)
+
+#define __asm_copy_to_user_6(to, from, ret) \
+	__asm_copy_to_user_6x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_to_user_7(to, from, ret) \
+	__asm_copy_to_user_6x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"	SETB [%0],D1Ar1\n"		\
+		"6:	SETB [%0++],D1Ar1\n",		\
+		"7:	ADD  %2,%2,#1\n",		\
+		"	.long 6b,7b\n")
+
+#define __asm_copy_to_user_8x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_to_user_4x_cont(to, from, ret,	\
+		"	GETD D1Ar1,[%1++]\n"		\
+		"	SETD [%0],D1Ar1\n"		\
+		"4:	SETD [%0++],D1Ar1\n" COPY,	\
+		"5:	ADD  %2,%2,#4\n"  FIXUP,	\
+		"	.long 4b,5b\n" TENTRY)
+
+#define __asm_copy_to_user_8(to, from, ret) \
+	__asm_copy_to_user_8x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_to_user_9(to, from, ret) \
+	__asm_copy_to_user_8x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"	SETB [%0],D1Ar1\n"		\
+		"6:	SETB [%0++],D1Ar1\n",		\
+		"7:	ADD  %2,%2,#1\n",		\
+		"	.long 6b,7b\n")
+
+#define __asm_copy_to_user_10x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_to_user_8x_cont(to, from, ret,	\
+		"	GETW D1Ar1,[%1++]\n"		\
+		"	SETW [%0],D1Ar1\n"		\
+		"6:	SETW [%0++],D1Ar1\n" COPY,	\
+		"7:	ADD  %2,%2,#2\n" FIXUP,		\
+		"	.long 6b,7b\n" TENTRY)
+
+#define __asm_copy_to_user_10(to, from, ret) \
+	__asm_copy_to_user_10x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_to_user_11(to, from, ret) \
+	__asm_copy_to_user_10x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"	SETB [%0],D1Ar1\n"		\
+		"8:	SETB [%0++],D1Ar1\n",		\
+		"9:	ADD  %2,%2,#1\n",		\
+		"	.long 8b,9b\n")
+
+#define __asm_copy_to_user_12x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_to_user_8x_cont(to, from, ret,	\
+		"	GETD D1Ar1,[%1++]\n"		\
+		"	SETD [%0],D1Ar1\n"		\
+		"6:	SETD [%0++],D1Ar1\n" COPY,	\
+		"7:	ADD  %2,%2,#4\n" FIXUP,		\
+		"	.long 6b,7b\n" TENTRY)
+#define __asm_copy_to_user_12(to, from, ret) \
+	__asm_copy_to_user_12x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_to_user_13(to, from, ret) \
+	__asm_copy_to_user_12x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"	SETB [%0],D1Ar1\n"		\
+		"8:	SETB [%0++],D1Ar1\n",		\
+		"9:	ADD  %2,%2,#1\n",		\
+		"	.long 8b,9b\n")
+
+#define __asm_copy_to_user_14x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_to_user_12x_cont(to, from, ret,	\
+		"	GETW D1Ar1,[%1++]\n"		\
+		"	SETW [%0],D1Ar1\n"		\
+		"8:	SETW [%0++],D1Ar1\n" COPY,	\
+		"9:	ADD  %2,%2,#2\n" FIXUP,		\
+		"	.long 8b,9b\n" TENTRY)
+
+#define __asm_copy_to_user_14(to, from, ret) \
+	__asm_copy_to_user_14x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_to_user_15(to, from, ret) \
+	__asm_copy_to_user_14x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"	SETB [%0],D1Ar1\n"		\
+		"10:	SETB [%0++],D1Ar1\n",		\
+		"11:	ADD  %2,%2,#1\n",		\
+		"	.long 10b,11b\n")
+
+#define __asm_copy_to_user_16x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_to_user_12x_cont(to, from, ret,	\
+		"	GETD D1Ar1,[%1++]\n"		\
+		"	SETD [%0],D1Ar1\n"		\
+		"8:	SETD [%0++],D1Ar1\n" COPY,	\
+		"9:	ADD  %2,%2,#4\n" FIXUP,		\
+		"	.long 8b,9b\n" TENTRY)
+
+#define __asm_copy_to_user_16(to, from, ret) \
+		__asm_copy_to_user_16x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_to_user_8x64(to, from, ret) \
+	__asm__ __volatile__ (				\
+		"	GETL D0Ar2,D1Ar1,[%1++]\n"	\
+		"	SETL [%0],D0Ar2,D1Ar1\n"	\
+		"2:	SETL [%0++],D0Ar2,D1Ar1\n"	\
+		"1:\n"					\
+		"	.section .fixup,\"ax\"\n"	\
+		"3:	ADD  %2,%2,#8\n"		\
+		"	MOVT    D0Ar2,#HI(1b)\n"	\
+		"	JUMP    D0Ar2,#LO(1b)\n"	\
+		"	.previous\n"			\
+		"	.section __ex_table,\"a\"\n"	\
+		"	.long 2b,3b\n"			\
+		"	.previous\n"			\
+		: "=r" (to), "=r" (from), "=r" (ret)	\
+		: "0" (to), "1" (from), "2" (ret)	\
+		: "D1Ar1", "D0Ar2", "memory")
+
+/*
+ *	optimized copying loop using RAPF when 64 bit aligned
+ *
+ *	n		will be automatically decremented inside the loop
+ *	ret		will be left intact. if error occurs we will rewind
+ *			so that the original non optimized code will fill up
+ *			this value correctly.
+ *
+ *	on fault:
+ *		>	n will hold total number of uncopied bytes
+ *
+ *		>	{'to','from'} will be rewind back so that
+ *			the non-optimized code will do the proper fix up
+ *
+ *	DCACHE drops the cacheline which helps in reducing cache
+ *	pollution.
+ *
+ *	We introduce an extra SETL at the end of the loop to
+ *	ensure we don't fall off the loop before we catch all
+ *	erros.
+ *
+ *	NOTICE:
+ *		LSM_STEP in TXSTATUS must be cleared in fix up code.
+ *		since we're using M{S,G}ETL, a fault might happen at
+ *		any address in the middle of M{S,G}ETL causing
+ *		the value of LSM_STEP to be incorrect which can
+ *		cause subsequent use of M{S,G}ET{L,D} to go wrong.
+ *		ie: if LSM_STEP was 1 when a fault occurs, the
+ *		next call to M{S,G}ET{L,D} will skip the first
+ *		copy/getting as it think that the first 1 has already
+ *		been done.
+ *
+ */
+#define __asm_copy_user_64bit_rapf_loop(				\
+		to, from, ret, n, id, FIXUP)				\
+	__asm__ __volatile__ (						\
+		".balign 8\n"						\
+		"MOV	RAPF, %1\n"					\
+		"MSETL	[A0StP++], D0Ar6, D0FrT, D0.5, D0.6, D0.7\n"	\
+		"MOV	D0Ar6, #0\n"					\
+		"LSR	D1Ar5, %3, #6\n"				\
+		"SUB	TXRPT, D1Ar5, #2\n"				\
+		"MOV	RAPF, %1\n"					\
+	"$Lloop"id":\n"							\
+		"ADD	RAPF, %1, #64\n"				\
+		"21:\n"							\
+		"MGETL	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"22:\n"							\
+		"MSETL	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #32\n"					\
+		"23:\n"							\
+		"MGETL	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"24:\n"							\
+		"MSETL	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #32\n"					\
+		"DCACHE	[%1+#-64], D0Ar6\n"				\
+		"BR	$Lloop"id"\n"					\
+									\
+		"MOV	RAPF, %1\n"					\
+		"25:\n"							\
+		"MGETL	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"26:\n"							\
+		"MSETL	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #32\n"					\
+		"27:\n"							\
+		"MGETL	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"28:\n"							\
+		"MSETL	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%0, %0, #8\n"					\
+		"29:\n"							\
+		"SETL	[%0++], D0.7, D1.7\n"				\
+		"SUB	%3, %3, #32\n"					\
+		"1:"							\
+		"DCACHE	[%1+#-64], D0Ar6\n"				\
+		"GETL    D0Ar6, D1Ar5, [A0StP+#-40]\n"			\
+		"GETL    D0FrT, D1RtP, [A0StP+#-32]\n"			\
+		"GETL    D0.5, D1.5, [A0StP+#-24]\n"			\
+		"GETL    D0.6, D1.6, [A0StP+#-16]\n"			\
+		"GETL    D0.7, D1.7, [A0StP+#-8]\n"			\
+		"SUB A0StP, A0StP, #40\n"				\
+		"	.section .fixup,\"ax\"\n"			\
+		"4:\n"							\
+		"	ADD	%0, %0, #8\n"				\
+		"3:\n"							\
+		"	MOV	D0Ar2, TXSTATUS\n"			\
+		"	MOV	D1Ar1, TXSTATUS\n"			\
+		"	AND	D1Ar1, D1Ar1, #0xFFFFF8FF\n"		\
+		"	MOV	TXSTATUS, D1Ar1\n"			\
+			FIXUP						\
+		"	MOVT    D0Ar2,#HI(1b)\n"			\
+		"	JUMP    D0Ar2,#LO(1b)\n"			\
+		"	.previous\n"					\
+		"	.section __ex_table,\"a\"\n"			\
+		"	.long 21b,3b\n"					\
+		"	.long 22b,3b\n"					\
+		"	.long 23b,3b\n"					\
+		"	.long 24b,3b\n"					\
+		"	.long 25b,3b\n"					\
+		"	.long 26b,3b\n"					\
+		"	.long 27b,3b\n"					\
+		"	.long 28b,3b\n"					\
+		"	.long 29b,4b\n"					\
+		"	.previous\n"					\
+		: "=r" (to), "=r" (from), "=r" (ret), "=d" (n)		\
+		: "0" (to), "1" (from), "2" (ret), "3" (n)		\
+		: "D1Ar1", "D0Ar2", "memory")
+
+/*	rewind 'to' and 'from'  pointers when a fault occurs
+ *
+ *	Rationale:
+ *		A fault always occurs on writing to user buffer. A fault
+ *		is at a single address, so we need to rewind by only 4
+ *		bytes.
+ *		Since we do a complete read from kernel buffer before
+ *		writing, we need to rewind it also. The amount to be
+ *		rewind equals the number of faulty writes in MSETD
+ *		which is: [4 - (LSM_STEP-1)]*8
+ *		LSM_STEP is bits 10:8 in TXSTATUS which is already read
+ *		and stored in D0Ar2
+ *
+ *		NOTE: If a fault occurs at the last operation in M{G,S}ETL
+ *			LSM_STEP will be 0. ie: we do 4 writes in our case, if
+ *			a fault happens at the 4th write, LSM_STEP will be 0
+ *			instead of 4. The code copes with that.
+ *
+ *		n is updated by the number of successful writes, which is:
+ *		n = n - (LSM_STEP-1)*8
+ */
+#define __asm_copy_to_user_64bit_rapf_loop(to,	from, ret, n, id)\
+	__asm_copy_user_64bit_rapf_loop(to, from, ret, n, id,		\
+		"LSR	D0Ar2, D0Ar2, #8\n"				\
+		"AND	D0Ar2, D0Ar2, #0x7\n"				\
+		"ADDZ	D0Ar2, D0Ar2, #4\n"				\
+		"SUB	D0Ar2, D0Ar2, #1\n"				\
+		"MOV	D1Ar1, #4\n"					\
+		"SUB	D0Ar2, D1Ar1, D0Ar2\n"				\
+		"LSL	D0Ar2, D0Ar2, #3\n"				\
+		"LSL	D1Ar1, D1Ar1, #3\n"				\
+		"SUB	D1Ar1, D1Ar1, D0Ar2\n"				\
+		"SUB	%0, %0, #8\n"					\
+		"SUB	%1,	%1,D0Ar2\n"				\
+		"SUB	%3, %3, D1Ar1\n")
+
+/*
+ *	optimized copying loop using RAPF when 32 bit aligned
+ *
+ *	n		will be automatically decremented inside the loop
+ *	ret		will be left intact. if error occurs we will rewind
+ *			so that the original non optimized code will fill up
+ *			this value correctly.
+ *
+ *	on fault:
+ *		>	n will hold total number of uncopied bytes
+ *
+ *		>	{'to','from'} will be rewind back so that
+ *			the non-optimized code will do the proper fix up
+ *
+ *	DCACHE drops the cacheline which helps in reducing cache
+ *	pollution.
+ *
+ *	We introduce an extra SETD at the end of the loop to
+ *	ensure we don't fall off the loop before we catch all
+ *	erros.
+ *
+ *	NOTICE:
+ *		LSM_STEP in TXSTATUS must be cleared in fix up code.
+ *		since we're using M{S,G}ETL, a fault might happen at
+ *		any address in the middle of M{S,G}ETL causing
+ *		the value of LSM_STEP to be incorrect which can
+ *		cause subsequent use of M{S,G}ET{L,D} to go wrong.
+ *		ie: if LSM_STEP was 1 when a fault occurs, the
+ *		next call to M{S,G}ET{L,D} will skip the first
+ *		copy/getting as it think that the first 1 has already
+ *		been done.
+ *
+ */
+#define __asm_copy_user_32bit_rapf_loop(				\
+			to,	from, ret, n, id, FIXUP)		\
+	__asm__ __volatile__ (						\
+		".balign 8\n"						\
+		"MOV	RAPF, %1\n"					\
+		"MSETL	[A0StP++], D0Ar6, D0FrT, D0.5, D0.6, D0.7\n"	\
+		"MOV	D0Ar6, #0\n"					\
+		"LSR	D1Ar5, %3, #6\n"				\
+		"SUB	TXRPT, D1Ar5, #2\n"				\
+		"MOV	RAPF, %1\n"					\
+	"$Lloop"id":\n"							\
+		"ADD	RAPF, %1, #64\n"				\
+		"21:\n"							\
+		"MGETD	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"22:\n"							\
+		"MSETD	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #16\n"					\
+		"23:\n"							\
+		"MGETD	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"24:\n"							\
+		"MSETD	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #16\n"					\
+		"25:\n"							\
+		"MGETD	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"26:\n"							\
+		"MSETD	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #16\n"					\
+		"27:\n"							\
+		"MGETD	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"28:\n"							\
+		"MSETD	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #16\n"					\
+		"DCACHE	[%1+#-64], D0Ar6\n"				\
+		"BR	$Lloop"id"\n"					\
+									\
+		"MOV	RAPF, %1\n"					\
+		"29:\n"							\
+		"MGETD	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"30:\n"							\
+		"MSETD	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #16\n"					\
+		"31:\n"							\
+		"MGETD	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"32:\n"							\
+		"MSETD	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #16\n"					\
+		"33:\n"							\
+		"MGETD	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"34:\n"							\
+		"MSETD	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%3, %3, #16\n"					\
+		"35:\n"							\
+		"MGETD	D0FrT, D0.5, D0.6, D0.7, [%1++]\n"		\
+		"36:\n"							\
+		"MSETD	[%0++], D0FrT, D0.5, D0.6, D0.7\n"		\
+		"SUB	%0, %0, #4\n"					\
+		"37:\n"							\
+		"SETD	[%0++], D0.7\n"					\
+		"SUB	%3, %3, #16\n"					\
+		"1:"							\
+		"DCACHE	[%1+#-64], D0Ar6\n"				\
+		"GETL    D0Ar6, D1Ar5, [A0StP+#-40]\n"			\
+		"GETL    D0FrT, D1RtP, [A0StP+#-32]\n"			\
+		"GETL    D0.5, D1.5, [A0StP+#-24]\n"			\
+		"GETL    D0.6, D1.6, [A0StP+#-16]\n"			\
+		"GETL    D0.7, D1.7, [A0StP+#-8]\n"			\
+		"SUB A0StP, A0StP, #40\n"				\
+		"	.section .fixup,\"ax\"\n"			\
+		"4:\n"							\
+		"	ADD		%0, %0, #4\n"			\
+		"3:\n"							\
+		"	MOV	D0Ar2, TXSTATUS\n"			\
+		"	MOV	D1Ar1, TXSTATUS\n"			\
+		"	AND	D1Ar1, D1Ar1, #0xFFFFF8FF\n"		\
+		"	MOV	TXSTATUS, D1Ar1\n"			\
+			FIXUP						\
+		"	MOVT    D0Ar2,#HI(1b)\n"			\
+		"	JUMP    D0Ar2,#LO(1b)\n"			\
+		"	.previous\n"					\
+		"	.section __ex_table,\"a\"\n"			\
+		"	.long 21b,3b\n"					\
+		"	.long 22b,3b\n"					\
+		"	.long 23b,3b\n"					\
+		"	.long 24b,3b\n"					\
+		"	.long 25b,3b\n"					\
+		"	.long 26b,3b\n"					\
+		"	.long 27b,3b\n"					\
+		"	.long 28b,3b\n"					\
+		"	.long 29b,3b\n"					\
+		"	.long 30b,3b\n"					\
+		"	.long 31b,3b\n"					\
+		"	.long 32b,3b\n"					\
+		"	.long 33b,3b\n"					\
+		"	.long 34b,3b\n"					\
+		"	.long 35b,3b\n"					\
+		"	.long 36b,3b\n"					\
+		"	.long 37b,4b\n"					\
+		"	.previous\n"					\
+		: "=r" (to), "=r" (from), "=r" (ret), "=d" (n)		\
+		: "0" (to), "1" (from), "2" (ret), "3" (n)		\
+		: "D1Ar1", "D0Ar2", "memory")
+
+/*	rewind 'to' and 'from'  pointers when a fault occurs
+ *
+ *	Rationale:
+ *		A fault always occurs on writing to user buffer. A fault
+ *		is at a single address, so we need to rewind by only 4
+ *		bytes.
+ *		Since we do a complete read from kernel buffer before
+ *		writing, we need to rewind it also. The amount to be
+ *		rewind equals the number of faulty writes in MSETD
+ *		which is: [4 - (LSM_STEP-1)]*4
+ *		LSM_STEP is bits 10:8 in TXSTATUS which is already read
+ *		and stored in D0Ar2
+ *
+ *		NOTE: If a fault occurs at the last operation in M{G,S}ETL
+ *			LSM_STEP will be 0. ie: we do 4 writes in our case, if
+ *			a fault happens at the 4th write, LSM_STEP will be 0
+ *			instead of 4. The code copes with that.
+ *
+ *		n is updated by the number of successful writes, which is:
+ *		n = n - (LSM_STEP-1)*4
+ */
+#define __asm_copy_to_user_32bit_rapf_loop(to, from, ret, n, id)\
+	__asm_copy_user_32bit_rapf_loop(to, from, ret, n, id,		\
+		"LSR	D0Ar2, D0Ar2, #8\n"				\
+		"AND	D0Ar2, D0Ar2, #0x7\n"				\
+		"ADDZ	D0Ar2, D0Ar2, #4\n"				\
+		"SUB	D0Ar2, D0Ar2, #1\n"				\
+		"MOV	D1Ar1, #4\n"					\
+		"SUB	D0Ar2, D1Ar1, D0Ar2\n"				\
+		"LSL	D0Ar2, D0Ar2, #2\n"				\
+		"LSL	D1Ar1, D1Ar1, #2\n"				\
+		"SUB	D1Ar1, D1Ar1, D0Ar2\n"				\
+		"SUB	%0, %0, #4\n"					\
+		"SUB	%1,	%1,	D0Ar2\n"			\
+		"SUB	%3, %3, D1Ar1\n")
+
+unsigned long __copy_user(void __user *pdst, const void *psrc,
+			  unsigned long n)
+{
+	register char __user *dst __asm__ ("A0.2") = pdst;
+	register const char *src __asm__ ("A1.2") = psrc;
+	unsigned long retn = 0;
+
+	if (n == 0)
+		return 0;
+
+	if ((unsigned long) src & 1) {
+		__asm_copy_to_user_1(dst, src, retn);
+		n--;
+	}
+	if ((unsigned long) dst & 1) {
+		/* Worst case - byte copy */
+		while (n > 0) {
+			__asm_copy_to_user_1(dst, src, retn);
+			n--;
+		}
+	}
+	if (((unsigned long) src & 2) && n >= 2) {
+		__asm_copy_to_user_2(dst, src, retn);
+		n -= 2;
+	}
+	if ((unsigned long) dst & 2) {
+		/* Second worst case - word copy */
+		while (n >= 2) {
+			__asm_copy_to_user_2(dst, src, retn);
+			n -= 2;
+		}
+	}
+
+#ifdef USE_RAPF
+	/* 64 bit copy loop */
+	if (!(((unsigned long) src | (__force unsigned long) dst) & 7)) {
+		if (n >= RAPF_MIN_BUF_SIZE) {
+			/* copy user using 64 bit rapf copy */
+			__asm_copy_to_user_64bit_rapf_loop(dst, src, retn,
+							n, "64cu");
+		}
+		while (n >= 8) {
+			__asm_copy_to_user_8x64(dst, src, retn);
+			n -= 8;
+		}
+	}
+	if (n >= RAPF_MIN_BUF_SIZE) {
+		/* copy user using 32 bit rapf copy */
+		__asm_copy_to_user_32bit_rapf_loop(dst, src, retn, n, "32cu");
+	}
+#else
+	/* 64 bit copy loop */
+	if (!(((unsigned long) src | (__force unsigned long) dst) & 7)) {
+		while (n >= 8) {
+			__asm_copy_to_user_8x64(dst, src, retn);
+			n -= 8;
+		}
+	}
+#endif
+
+	while (n >= 16) {
+		__asm_copy_to_user_16(dst, src, retn);
+		n -= 16;
+	}
+
+	while (n >= 4) {
+		__asm_copy_to_user_4(dst, src, retn);
+		n -= 4;
+	}
+
+	switch (n) {
+	case 0:
+		break;
+	case 1:
+		__asm_copy_to_user_1(dst, src, retn);
+		break;
+	case 2:
+		__asm_copy_to_user_2(dst, src, retn);
+		break;
+	case 3:
+		__asm_copy_to_user_3(dst, src, retn);
+		break;
+	}
+
+	return retn;
+}
+
+#define __asm_copy_from_user_1(to, from, ret) \
+	__asm_copy_user_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"	\
+		"2:	SETB [%0++],D1Ar1\n",	\
+		"3:	ADD  %2,%2,#1\n"	\
+		"	SETB [%0++],D1Ar1\n",	\
+		"	.long 2b,3b\n")
+
+#define __asm_copy_from_user_2x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_user_cont(to, from, ret,		\
+		"	GETW D1Ar1,[%1++]\n"		\
+		"2:	SETW [%0++],D1Ar1\n" COPY,	\
+		"3:	ADD  %2,%2,#2\n"		\
+		"	SETW [%0++],D1Ar1\n" FIXUP,	\
+		"	.long 2b,3b\n" TENTRY)
+
+#define __asm_copy_from_user_2(to, from, ret) \
+	__asm_copy_from_user_2x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_from_user_3(to, from, ret)		\
+	__asm_copy_from_user_2x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"4:	SETB [%0++],D1Ar1\n",		\
+		"5:	ADD  %2,%2,#1\n"		\
+		"	SETB [%0++],D1Ar1\n",		\
+		"	.long 4b,5b\n")
+
+#define __asm_copy_from_user_4x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_user_cont(to, from, ret,		\
+		"	GETD D1Ar1,[%1++]\n"		\
+		"2:	SETD [%0++],D1Ar1\n" COPY,	\
+		"3:	ADD  %2,%2,#4\n"		\
+		"	SETD [%0++],D1Ar1\n" FIXUP,	\
+		"	.long 2b,3b\n" TENTRY)
+
+#define __asm_copy_from_user_4(to, from, ret) \
+	__asm_copy_from_user_4x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_from_user_5(to, from, ret) \
+	__asm_copy_from_user_4x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"4:	SETB [%0++],D1Ar1\n",		\
+		"5:	ADD  %2,%2,#1\n"		\
+		"	SETB [%0++],D1Ar1\n",		\
+		"	.long 4b,5b\n")
+
+#define __asm_copy_from_user_6x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_from_user_4x_cont(to, from, ret,	\
+		"	GETW D1Ar1,[%1++]\n"		\
+		"4:	SETW [%0++],D1Ar1\n" COPY,	\
+		"5:	ADD  %2,%2,#2\n"		\
+		"	SETW [%0++],D1Ar1\n" FIXUP,	\
+		"	.long 4b,5b\n" TENTRY)
+
+#define __asm_copy_from_user_6(to, from, ret) \
+	__asm_copy_from_user_6x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_from_user_7(to, from, ret) \
+	__asm_copy_from_user_6x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"6:	SETB [%0++],D1Ar1\n",		\
+		"7:	ADD  %2,%2,#1\n"		\
+		"	SETB [%0++],D1Ar1\n",		\
+		"	.long 6b,7b\n")
+
+#define __asm_copy_from_user_8x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_from_user_4x_cont(to, from, ret,	\
+		"	GETD D1Ar1,[%1++]\n"		\
+		"4:	SETD [%0++],D1Ar1\n" COPY,	\
+		"5:	ADD  %2,%2,#4\n"			\
+		"	SETD [%0++],D1Ar1\n" FIXUP,		\
+		"	.long 4b,5b\n" TENTRY)
+
+#define __asm_copy_from_user_8(to, from, ret) \
+	__asm_copy_from_user_8x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_from_user_9(to, from, ret) \
+	__asm_copy_from_user_8x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"6:	SETB [%0++],D1Ar1\n",		\
+		"7:	ADD  %2,%2,#1\n"		\
+		"	SETB [%0++],D1Ar1\n",		\
+		"	.long 6b,7b\n")
+
+#define __asm_copy_from_user_10x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_from_user_8x_cont(to, from, ret,	\
+		"	GETW D1Ar1,[%1++]\n"		\
+		"6:	SETW [%0++],D1Ar1\n" COPY,	\
+		"7:	ADD  %2,%2,#2\n"		\
+		"	SETW [%0++],D1Ar1\n" FIXUP,	\
+		"	.long 6b,7b\n" TENTRY)
+
+#define __asm_copy_from_user_10(to, from, ret) \
+	__asm_copy_from_user_10x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_from_user_11(to, from, ret)		\
+	__asm_copy_from_user_10x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"8:	SETB [%0++],D1Ar1\n",		\
+		"9:	ADD  %2,%2,#1\n"		\
+		"	SETB [%0++],D1Ar1\n",		\
+		"	.long 8b,9b\n")
+
+#define __asm_copy_from_user_12x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_from_user_8x_cont(to, from, ret,	\
+		"	GETD D1Ar1,[%1++]\n"		\
+		"6:	SETD [%0++],D1Ar1\n" COPY,	\
+		"7:	ADD  %2,%2,#4\n"		\
+		"	SETD [%0++],D1Ar1\n" FIXUP,	\
+		"	.long 6b,7b\n" TENTRY)
+
+#define __asm_copy_from_user_12(to, from, ret) \
+	__asm_copy_from_user_12x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_from_user_13(to, from, ret) \
+	__asm_copy_from_user_12x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"8:	SETB [%0++],D1Ar1\n",		\
+		"9:	ADD  %2,%2,#1\n"		\
+		"	SETB [%0++],D1Ar1\n",		\
+		"	.long 8b,9b\n")
+
+#define __asm_copy_from_user_14x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_from_user_12x_cont(to, from, ret,	\
+		"	GETW D1Ar1,[%1++]\n"		\
+		"8:	SETW [%0++],D1Ar1\n" COPY,	\
+		"9:	ADD  %2,%2,#2\n"		\
+		"	SETW [%0++],D1Ar1\n" FIXUP,	\
+		"	.long 8b,9b\n" TENTRY)
+
+#define __asm_copy_from_user_14(to, from, ret) \
+	__asm_copy_from_user_14x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_from_user_15(to, from, ret) \
+	__asm_copy_from_user_14x_cont(to, from, ret,	\
+		"	GETB D1Ar1,[%1++]\n"		\
+		"10:	SETB [%0++],D1Ar1\n",		\
+		"11:	ADD  %2,%2,#1\n"		\
+		"	SETB [%0++],D1Ar1\n",		\
+		"	.long 10b,11b\n")
+
+#define __asm_copy_from_user_16x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
+	__asm_copy_from_user_12x_cont(to, from, ret,	\
+		"	GETD D1Ar1,[%1++]\n"		\
+		"8:	SETD [%0++],D1Ar1\n" COPY,	\
+		"9:	ADD  %2,%2,#4\n"		\
+		"	SETD [%0++],D1Ar1\n" FIXUP,	\
+		"	.long 8b,9b\n" TENTRY)
+
+#define __asm_copy_from_user_16(to, from, ret) \
+	__asm_copy_from_user_16x_cont(to, from, ret, "", "", "")
+
+#define __asm_copy_from_user_8x64(to, from, ret) \
+	__asm__ __volatile__ (				\
+		"	GETL D0Ar2,D1Ar1,[%1++]\n"	\
+		"2:	SETL [%0++],D0Ar2,D1Ar1\n"	\
+		"1:\n"					\
+		"	.section .fixup,\"ax\"\n"	\
+		"	MOV D1Ar1,#0\n"			\
+		"	MOV D0Ar2,#0\n"			\
+		"3:	ADD  %2,%2,#8\n"		\
+		"	SETL [%0++],D0Ar2,D1Ar1\n"	\
+		"	MOVT    D0Ar2,#HI(1b)\n"	\
+		"	JUMP    D0Ar2,#LO(1b)\n"	\
+		"	.previous\n"			\
+		"	.section __ex_table,\"a\"\n"	\
+		"	.long 2b,3b\n"			\
+		"	.previous\n"			\
+		: "=a" (to), "=r" (from), "=r" (ret)	\
+		: "0" (to), "1" (from), "2" (ret)	\
+		: "D1Ar1", "D0Ar2", "memory")
+
+/*	rewind 'from' pointer when a fault occurs
+ *
+ *	Rationale:
+ *		A fault occurs while reading from user buffer, which is the
+ *		source. Since the fault is at a single address, we only
+ *		need to rewind by 8 bytes.
+ *		Since we don't write to kernel buffer until we read first,
+ *		the kernel buffer is at the right state and needn't be
+ *		corrected.
+ */
+#define __asm_copy_from_user_64bit_rapf_loop(to, from, ret, n, id)	\
+	__asm_copy_user_64bit_rapf_loop(to, from, ret, n, id,		\
+		"SUB	%1, %1, #8\n")
+
+/*	rewind 'from' pointer when a fault occurs
+ *
+ *	Rationale:
+ *		A fault occurs while reading from user buffer, which is the
+ *		source. Since the fault is at a single address, we only
+ *		need to rewind by 4 bytes.
+ *		Since we don't write to kernel buffer until we read first,
+ *		the kernel buffer is at the right state and needn't be
+ *		corrected.
+ */
+#define __asm_copy_from_user_32bit_rapf_loop(to, from, ret, n, id)	\
+	__asm_copy_user_32bit_rapf_loop(to, from, ret, n, id,		\
+		"SUB	%1, %1, #4\n")
+
+
+/* Copy from user to kernel, zeroing the bytes that were inaccessible in
+   userland.  The return-value is the number of bytes that were
+   inaccessible.  */
+unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
+				  unsigned long n)
+{
+	register char *dst __asm__ ("A0.2") = pdst;
+	register const char __user *src __asm__ ("A1.2") = psrc;
+	unsigned long retn = 0;
+
+	if (n == 0)
+		return 0;
+
+	if ((unsigned long) src & 1) {
+		__asm_copy_from_user_1(dst, src, retn);
+		n--;
+	}
+	if ((unsigned long) dst & 1) {
+		/* Worst case - byte copy */
+		while (n > 0) {
+			__asm_copy_from_user_1(dst, src, retn);
+			n--;
+			if (retn)
+				goto copy_exception_bytes;
+		}
+	}
+	if (((unsigned long) src & 2) && n >= 2) {
+		__asm_copy_from_user_2(dst, src, retn);
+		n -= 2;
+	}
+	if ((unsigned long) dst & 2) {
+		/* Second worst case - word copy */
+		while (n >= 2) {
+			__asm_copy_from_user_2(dst, src, retn);
+			n -= 2;
+			if (retn)
+				goto copy_exception_bytes;
+		}
+	}
+
+	/* We only need one check after the unalignment-adjustments,
+	   because if both adjustments were done, either both or
+	   neither reference had an exception.  */
+	if (retn != 0)
+		goto copy_exception_bytes;
+
+#ifdef USE_RAPF
+	/* 64 bit copy loop */
+	if (!(((unsigned long) src | (unsigned long) dst) & 7)) {
+		if (n >= RAPF_MIN_BUF_SIZE) {
+			/* Copy using fast 64bit rapf */
+			__asm_copy_from_user_64bit_rapf_loop(dst, src, retn,
+							n, "64cuz");
+		}
+		while (n >= 8) {
+			__asm_copy_from_user_8x64(dst, src, retn);
+			n -= 8;
+			if (retn)
+				goto copy_exception_bytes;
+		}
+	}
+
+	if (n >= RAPF_MIN_BUF_SIZE) {
+		/* Copy using fast 32bit rapf */
+		__asm_copy_from_user_32bit_rapf_loop(dst, src, retn,
+						n, "32cuz");
+	}
+#else
+	/* 64 bit copy loop */
+	if (!(((unsigned long) src | (unsigned long) dst) & 7)) {
+		while (n >= 8) {
+			__asm_copy_from_user_8x64(dst, src, retn);
+			n -= 8;
+			if (retn)
+				goto copy_exception_bytes;
+		}
+	}
+#endif
+
+	while (n >= 4) {
+		__asm_copy_from_user_4(dst, src, retn);
+		n -= 4;
+
+		if (retn)
+			goto copy_exception_bytes;
+	}
+
+	/* If we get here, there were no memory read faults.  */
+	switch (n) {
+		/* These copies are at least "naturally aligned" (so we don't
+		   have to check each byte), due to the src alignment code.
+		   The *_3 case *will* get the correct count for retn.  */
+	case 0:
+		/* This case deliberately left in (if you have doubts check the
+		   generated assembly code).  */
+		break;
+	case 1:
+		__asm_copy_from_user_1(dst, src, retn);
+		break;
+	case 2:
+		__asm_copy_from_user_2(dst, src, retn);
+		break;
+	case 3:
+		__asm_copy_from_user_3(dst, src, retn);
+		break;
+	}
+
+	/* If we get here, retn correctly reflects the number of failing
+	   bytes.  */
+	return retn;
+
+ copy_exception_bytes:
+	/* We already have "retn" bytes cleared, and need to clear the
+	   remaining "n" bytes.  A non-optimized simple byte-for-byte in-line
+	   memset is preferred here, since this isn't speed-critical code and
+	   we'd rather have this a leaf-function than calling memset.  */
+	{
+		char *endp;
+		for (endp = dst + n; dst < endp; dst++)
+			*dst = 0;
+	}
+
+	return retn + n;
+}
+
+#define __asm_clear_8x64(to, ret) \
+	__asm__ __volatile__ (				\
+		"	MOV  D0Ar2,#0\n"		\
+		"	MOV  D1Ar1,#0\n"		\
+		"	SETL [%0],D0Ar2,D1Ar1\n"	\
+		"2:	SETL [%0++],D0Ar2,D1Ar1\n"	\
+		"1:\n"					\
+		"	.section .fixup,\"ax\"\n"	\
+		"3:	ADD  %1,%1,#8\n"		\
+		"	MOVT    D0Ar2,#HI(1b)\n"	\
+		"	JUMP    D0Ar2,#LO(1b)\n"	\
+		"	.previous\n"			\
+		"	.section __ex_table,\"a\"\n"	\
+		"	.long 2b,3b\n"			\
+		"	.previous\n"			\
+		: "=r" (to), "=r" (ret)	\
+		: "0" (to), "1" (ret)	\
+		: "D1Ar1", "D0Ar2", "memory")
+
+/* Zero userspace.  */
+
+#define __asm_clear(to, ret, CLEAR, FIXUP, TENTRY) \
+	__asm__ __volatile__ (				\
+		"	MOV D1Ar1,#0\n"			\
+			CLEAR				\
+		"1:\n"					\
+		"	.section .fixup,\"ax\"\n"	\
+			FIXUP				\
+		"	MOVT    D1Ar1,#HI(1b)\n"	\
+		"	JUMP    D1Ar1,#LO(1b)\n"	\
+		"	.previous\n"			\
+		"	.section __ex_table,\"a\"\n"	\
+			TENTRY				\
+		"	.previous"			\
+		: "=r" (to), "=r" (ret)			\
+		: "0" (to), "1" (ret)			\
+		: "D1Ar1", "memory")
+
+#define __asm_clear_1(to, ret) \
+	__asm_clear(to, ret,			\
+		"	SETB [%0],D1Ar1\n"	\
+		"2:	SETB [%0++],D1Ar1\n",	\
+		"3:	ADD  %1,%1,#1\n",	\
+		"	.long 2b,3b\n")
+
+#define __asm_clear_2(to, ret) \
+	__asm_clear(to, ret,			\
+		"	SETW [%0],D1Ar1\n"	\
+		"2:	SETW [%0++],D1Ar1\n",	\
+		"3:	ADD  %1,%1,#2\n",	\
+		"	.long 2b,3b\n")
+
+#define __asm_clear_3(to, ret) \
+	__asm_clear(to, ret,			\
+		 "2:	SETW [%0++],D1Ar1\n"	\
+		 "	SETB [%0],D1Ar1\n"	\
+		 "3:	SETB [%0++],D1Ar1\n",	\
+		 "4:	ADD  %1,%1,#2\n"	\
+		 "5:	ADD  %1,%1,#1\n",	\
+		 "	.long 2b,4b\n"		\
+		 "	.long 3b,5b\n")
+
+#define __asm_clear_4x_cont(to, ret, CLEAR, FIXUP, TENTRY) \
+	__asm_clear(to, ret,				\
+		"	SETD [%0],D1Ar1\n"		\
+		"2:	SETD [%0++],D1Ar1\n" CLEAR,	\
+		"3:	ADD  %1,%1,#4\n" FIXUP,		\
+		"	.long 2b,3b\n" TENTRY)
+
+#define __asm_clear_4(to, ret) \
+	__asm_clear_4x_cont(to, ret, "", "", "")
+
+#define __asm_clear_8x_cont(to, ret, CLEAR, FIXUP, TENTRY) \
+	__asm_clear_4x_cont(to, ret,			\
+		"	SETD [%0],D1Ar1\n"		\
+		"4:	SETD [%0++],D1Ar1\n" CLEAR,	\
+		"5:	ADD  %1,%1,#4\n" FIXUP,		\
+		"	.long 4b,5b\n" TENTRY)
+
+#define __asm_clear_8(to, ret) \
+	__asm_clear_8x_cont(to, ret, "", "", "")
+
+#define __asm_clear_12x_cont(to, ret, CLEAR, FIXUP, TENTRY) \
+	__asm_clear_8x_cont(to, ret,			\
+		"	SETD [%0],D1Ar1\n"		\
+		"6:	SETD [%0++],D1Ar1\n" CLEAR,	\
+		"7:	ADD  %1,%1,#4\n" FIXUP,		\
+		"	.long 6b,7b\n" TENTRY)
+
+#define __asm_clear_12(to, ret) \
+	__asm_clear_12x_cont(to, ret, "", "", "")
+
+#define __asm_clear_16x_cont(to, ret, CLEAR, FIXUP, TENTRY) \
+	__asm_clear_12x_cont(to, ret,			\
+		"	SETD [%0],D1Ar1\n"		\
+		"8:	SETD [%0++],D1Ar1\n" CLEAR,	\
+		"9:	ADD  %1,%1,#4\n" FIXUP,		\
+		"	.long 8b,9b\n" TENTRY)
+
+#define __asm_clear_16(to, ret) \
+	__asm_clear_16x_cont(to, ret, "", "", "")
+
+unsigned long __do_clear_user(void __user *pto, unsigned long pn)
+{
+	register char __user *dst __asm__ ("D0Re0") = pto;
+	register unsigned long n __asm__ ("D1Re0") = pn;
+	register unsigned long retn __asm__ ("D0Ar6") = 0;
+
+	if ((unsigned long) dst & 1) {
+		__asm_clear_1(dst, retn);
+		n--;
+	}
+
+	if ((unsigned long) dst & 2) {
+		__asm_clear_2(dst, retn);
+		n -= 2;
+	}
+
+	/* 64 bit copy loop */
+	if (!((__force unsigned long) dst & 7)) {
+		while (n >= 8) {
+			__asm_clear_8x64(dst, retn);
+			n -= 8;
+		}
+	}
+
+	while (n >= 16) {
+		__asm_clear_16(dst, retn);
+		n -= 16;
+	}
+
+	while (n >= 4) {
+		__asm_clear_4(dst, retn);
+		n -= 4;
+	}
+
+	switch (n) {
+	case 0:
+		break;
+	case 1:
+		__asm_clear_1(dst, retn);
+		break;
+	case 2:
+		__asm_clear_2(dst, retn);
+		break;
+	case 3:
+		__asm_clear_3(dst, retn);
+		break;
+	}
+
+	return retn;
+}
+
+unsigned char __get_user_asm_b(const void __user *addr, long *err)
+{
+	register unsigned char x __asm__ ("D0Re0") = 0;
+	__asm__ __volatile__(
+		"	GETB %0,[%2]\n"
+		"1:\n"
+		"	GETB %0,[%2]\n"
+		"2:\n"
+		"	.section .fixup,\"ax\"\n"
+		"3:	MOV     D0FrT,%3\n"
+		"	SETD    [%1],D0FrT\n"
+		"	MOVT    D0FrT,#HI(2b)\n"
+		"	JUMP    D0FrT,#LO(2b)\n"
+		"	.previous\n"
+		"	.section __ex_table,\"a\"\n"
+		"	.long 1b,3b\n"
+		"	.previous\n"
+		: "=r" (x)
+		: "r" (err), "r" (addr), "P" (-EFAULT)
+		: "D0FrT");
+	return x;
+}
+
+unsigned short __get_user_asm_w(const void __user *addr, long *err)
+{
+	register unsigned short x __asm__ ("D0Re0") = 0;
+	__asm__ __volatile__(
+		"	GETW %0,[%2]\n"
+		"1:\n"
+		"	GETW %0,[%2]\n"
+		"2:\n"
+		"	.section .fixup,\"ax\"\n"
+		"3:	MOV     D0FrT,%3\n"
+		"	SETD    [%1],D0FrT\n"
+		"	MOVT    D0FrT,#HI(2b)\n"
+		"	JUMP    D0FrT,#LO(2b)\n"
+		"	.previous\n"
+		"	.section __ex_table,\"a\"\n"
+		"	.long 1b,3b\n"
+		"	.previous\n"
+		: "=r" (x)
+		: "r" (err), "r" (addr), "P" (-EFAULT)
+		: "D0FrT");
+	return x;
+}
+
+unsigned int __get_user_asm_d(const void __user *addr, long *err)
+{
+	register unsigned int x __asm__ ("D0Re0") = 0;
+	__asm__ __volatile__(
+		"	GETD %0,[%2]\n"
+		"1:\n"
+		"	GETD %0,[%2]\n"
+		"2:\n"
+		"	.section .fixup,\"ax\"\n"
+		"3:	MOV     D0FrT,%3\n"
+		"	SETD    [%1],D0FrT\n"
+		"	MOVT    D0FrT,#HI(2b)\n"
+		"	JUMP    D0FrT,#LO(2b)\n"
+		"	.previous\n"
+		"	.section __ex_table,\"a\"\n"
+		"	.long 1b,3b\n"
+		"	.previous\n"
+		: "=r" (x)
+		: "r" (err), "r" (addr), "P" (-EFAULT)
+		: "D0FrT");
+	return x;
+}
+
+long __put_user_asm_b(unsigned int x, void __user *addr)
+{
+	register unsigned int err __asm__ ("D0Re0") = 0;
+	__asm__ __volatile__(
+		"	MOV  %0,#0\n"
+		"	SETB [%2],%1\n"
+		"1:\n"
+		"	SETB [%2],%1\n"
+		"2:\n"
+		".section .fixup,\"ax\"\n"
+		"3:	MOV     %0,%3\n"
+		"	MOVT    D0FrT,#HI(2b)\n"
+		"	JUMP    D0FrT,#LO(2b)\n"
+		".previous\n"
+		".section __ex_table,\"a\"\n"
+		"	.long 1b,3b\n"
+		".previous"
+		: "=r"(err)
+		: "d" (x), "a" (addr), "P"(-EFAULT)
+		: "D0FrT");
+	return err;
+}
+
+long __put_user_asm_w(unsigned int x, void __user *addr)
+{
+	register unsigned int err __asm__ ("D0Re0") = 0;
+	__asm__ __volatile__(
+		"	MOV  %0,#0\n"
+		"	SETW [%2],%1\n"
+		"1:\n"
+		"	SETW [%2],%1\n"
+		"2:\n"
+		".section .fixup,\"ax\"\n"
+		"3:	MOV     %0,%3\n"
+		"	MOVT    D0FrT,#HI(2b)\n"
+		"	JUMP    D0FrT,#LO(2b)\n"
+		".previous\n"
+		".section __ex_table,\"a\"\n"
+		"	.long 1b,3b\n"
+		".previous"
+		: "=r"(err)
+		: "d" (x), "a" (addr), "P"(-EFAULT)
+		: "D0FrT");
+	return err;
+}
+
+long __put_user_asm_d(unsigned int x, void __user *addr)
+{
+	register unsigned int err __asm__ ("D0Re0") = 0;
+	__asm__ __volatile__(
+		"	MOV  %0,#0\n"
+		"	SETD [%2],%1\n"
+		"1:\n"
+		"	SETD [%2],%1\n"
+		"2:\n"
+		".section .fixup,\"ax\"\n"
+		"3:	MOV     %0,%3\n"
+		"	MOVT    D0FrT,#HI(2b)\n"
+		"	JUMP    D0FrT,#LO(2b)\n"
+		".previous\n"
+		".section __ex_table,\"a\"\n"
+		"	.long 1b,3b\n"
+		".previous"
+		: "=r"(err)
+		: "d" (x), "a" (addr), "P"(-EFAULT)
+		: "D0FrT");
+	return err;
+}
+
+long __put_user_asm_l(unsigned long long x, void __user *addr)
+{
+	register unsigned int err __asm__ ("D0Re0") = 0;
+	__asm__ __volatile__(
+		"	MOV  %0,#0\n"
+		"	SETL [%2],%1,%t1\n"
+		"1:\n"
+		"	SETL [%2],%1,%t1\n"
+		"2:\n"
+		".section .fixup,\"ax\"\n"
+		"3:	MOV     %0,%3\n"
+		"	MOVT    D0FrT,#HI(2b)\n"
+		"	JUMP    D0FrT,#LO(2b)\n"
+		".previous\n"
+		".section __ex_table,\"a\"\n"
+		"	.long 1b,3b\n"
+		".previous"
+		: "=r"(err)
+		: "d" (x), "a" (addr), "P"(-EFAULT)
+		: "D0FrT");
+	return err;
+}
+
+long strnlen_user(const char __user *src, long count)
+{
+	long res;
+
+	if (!access_ok(VERIFY_READ, src, 0))
+		return 0;
+
+	asm volatile ("	MOV     D0Ar4, %1\n"
+		      "	MOV     D0Ar6, %2\n"
+		      "0:\n"
+		      "	SUBS    D0FrT, D0Ar6, #0\n"
+		      "	SUB     D0Ar6, D0Ar6, #1\n"
+		      "	BLE     2f\n"
+		      "	GETB    D0FrT, [D0Ar4+#1++]\n"
+		      "1:\n"
+		      "	TST     D0FrT, #255\n"
+		      "	BNE     0b\n"
+		      "2:\n"
+		      "	SUB     %0, %2, D0Ar6\n"
+		      "3:\n"
+		      "	.section .fixup,\"ax\"\n"
+		      "4:\n"
+		      "	MOV     %0, #0\n"
+		      "	MOVT    D0FrT,#HI(3b)\n"
+		      "	JUMP    D0FrT,#LO(3b)\n"
+		      "	.previous\n"
+		      "	.section __ex_table,\"a\"\n"
+		      "	.long 1b,4b\n"
+		      "	.previous\n"
+		      : "=r" (res)
+		      : "r" (src), "r" (count)
+		      : "D0FrT", "D0Ar4", "D0Ar6", "cc");
+
+	return res;
+}
+
+long __strncpy_from_user(char *dst, const char __user *src, long count)
+{
+	long res;
+
+	if (count == 0)
+		return 0;
+
+	/*
+	 * Currently, in 2.4.0-test9, most ports use a simple byte-copy loop.
+	 *  So do we.
+	 *
+	 *  This code is deduced from:
+	 *
+	 *      char tmp2;
+	 *      long tmp1, tmp3;
+	 *      tmp1 = count;
+	 *      while ((*dst++ = (tmp2 = *src++)) != 0
+	 *             && --tmp1)
+	 *        ;
+	 *
+	 *      res = count - tmp1;
+	 *
+	 *  with tweaks.
+	 */
+
+	__asm__ __volatile__("	MOV  %0,%3\n"
+			     "1:\n"
+			     "	GETB D0FrT,[%2++]\n"
+			     "2:\n"
+			     "	CMP  D0FrT,#0\n"
+			     "	SETB [%1++],D0FrT\n"
+			     "	BEQ  3f\n"
+			     "	SUBS %0,%0,#1\n"
+			     "	BNZ  1b\n"
+			     "3:\n"
+			     "	SUB  %0,%3,%0\n"
+			     "4:\n"
+			     "	.section .fixup,\"ax\"\n"
+			     "5:\n"
+			     "	MOV  %0,%7\n"
+			     "	MOVT    D0FrT,#HI(4b)\n"
+			     "	JUMP    D0FrT,#LO(4b)\n"
+			     "	.previous\n"
+			     "	.section __ex_table,\"a\"\n"
+			     "	.long 2b,5b\n"
+			     "	.previous"
+			     : "=r" (res), "=r" (dst), "=r" (src), "=r" (count)
+			     : "3" (count), "1" (dst), "2" (src), "P" (-EFAULT)
+			     : "D0FrT", "memory", "cc");
+
+	return res;
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 26/40] metag: Stack unwinding
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (22 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 25/40] metag: optimised library functions James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 27/40] metag: various other headers James Hogan
                   ` (16 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add stack unwinding support for Meta.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/stacktrace.h |   20 ++++
 arch/metag/kernel/stacktrace.c      |  187 +++++++++++++++++++++++++++++++++++
 2 files changed, 207 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/stacktrace.h
 create mode 100644 arch/metag/kernel/stacktrace.c

diff --git a/arch/metag/include/asm/stacktrace.h b/arch/metag/include/asm/stacktrace.h
new file mode 100644
index 0000000..2830a0f
--- /dev/null
+++ b/arch/metag/include/asm/stacktrace.h
@@ -0,0 +1,20 @@
+#ifndef __ASM_STACKTRACE_H
+#define __ASM_STACKTRACE_H
+
+struct stackframe {
+	unsigned long fp;
+	unsigned long sp;
+	unsigned long lr;
+	unsigned long pc;
+};
+
+struct metag_frame {
+	unsigned long fp;
+	unsigned long lr;
+};
+
+extern int unwind_frame(struct stackframe *frame);
+extern void walk_stackframe(struct stackframe *frame,
+			    int (*fn)(struct stackframe *, void *), void *data);
+
+#endif	/* __ASM_STACKTRACE_H */
diff --git a/arch/metag/kernel/stacktrace.c b/arch/metag/kernel/stacktrace.c
new file mode 100644
index 0000000..5510361
--- /dev/null
+++ b/arch/metag/kernel/stacktrace.c
@@ -0,0 +1,187 @@
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+
+#include <asm/stacktrace.h>
+
+#if defined(CONFIG_FRAME_POINTER)
+
+#ifdef CONFIG_KALLSYMS
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+
+static unsigned long tbi_boing_addr;
+static unsigned long tbi_boing_size;
+
+static void tbi_boing_init(void)
+{
+	/* We need to know where TBIBoingVec is and it's size */
+	unsigned long size;
+	unsigned long offset;
+	char modname[MODULE_NAME_LEN];
+	char name[KSYM_NAME_LEN];
+	tbi_boing_addr = kallsyms_lookup_name("___TBIBoingVec");
+	if (!tbi_boing_addr)
+		tbi_boing_addr = 1;
+	else if (!lookup_symbol_attrs(tbi_boing_addr, &size,
+				      &offset, modname, name))
+		tbi_boing_size = size;
+}
+#endif
+
+#define ALIGN_DOWN(addr, size)  ((addr)&(~((size)-1)))
+
+/*
+ * Unwind the current stack frame and store the new register values in the
+ * structure passed as argument. Unwinding is equivalent to a function return,
+ * hence the new PC value rather than LR should be used for backtrace.
+ */
+int notrace unwind_frame(struct stackframe *frame)
+{
+	struct metag_frame *fp = (struct metag_frame *)frame->fp;
+	unsigned long lr;
+	unsigned long fpnew;
+
+	if (frame->fp & 0x7)
+		return -EINVAL;
+
+	fpnew = fp->fp;
+	lr = fp->lr - 4;
+
+#ifdef CONFIG_KALLSYMS
+	/* If we've reached TBIBoingVec then we're at an interrupt
+	 * entry point or a syscall entry point. The frame pointer
+	 * points to a pt_regs which can be used to continue tracing on
+	 * the other side of the boing.
+	 */
+	if (!tbi_boing_addr)
+		tbi_boing_init();
+	if (tbi_boing_size && lr >= tbi_boing_addr &&
+	    lr < tbi_boing_addr + tbi_boing_size) {
+		struct pt_regs *regs = (struct pt_regs *)fpnew;
+		if (user_mode(regs))
+			return -EINVAL;
+		fpnew = regs->ctx.AX[1].U0;
+		lr = regs->ctx.DX[4].U1;
+	}
+#endif
+
+	/* stack grows up, so frame pointers must decrease */
+	if (fpnew < (ALIGN_DOWN((unsigned long)fp, THREAD_SIZE) +
+		     sizeof(struct thread_info)) || fpnew >= (unsigned long)fp)
+		return -EINVAL;
+
+	/* restore the registers from the stack frame */
+	frame->fp = fpnew;
+	frame->pc = lr;
+
+	return 0;
+}
+#else
+int notrace unwind_frame(struct stackframe *frame)
+{
+	struct metag_frame *sp = (struct metag_frame *)frame->sp;
+
+	if (frame->sp & 0x7)
+		return -EINVAL;
+
+	while (!kstack_end(sp)) {
+		unsigned long addr = sp->lr - 4;
+		sp--;
+
+		if (__kernel_text_address(addr)) {
+			frame->sp = (unsigned long)sp;
+			frame->pc = addr;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+#endif
+
+void notrace walk_stackframe(struct stackframe *frame,
+		     int (*fn)(struct stackframe *, void *), void *data)
+{
+	while (1) {
+		int ret;
+
+		if (fn(frame, data))
+			break;
+		ret = unwind_frame(frame);
+		if (ret < 0)
+			break;
+	}
+}
+EXPORT_SYMBOL(walk_stackframe);
+
+#ifdef CONFIG_STACKTRACE
+struct stack_trace_data {
+	struct stack_trace *trace;
+	unsigned int no_sched_functions;
+	unsigned int skip;
+};
+
+static int save_trace(struct stackframe *frame, void *d)
+{
+	struct stack_trace_data *data = d;
+	struct stack_trace *trace = data->trace;
+	unsigned long addr = frame->pc;
+
+	if (data->no_sched_functions && in_sched_functions(addr))
+		return 0;
+	if (data->skip) {
+		data->skip--;
+		return 0;
+	}
+
+	trace->entries[trace->nr_entries++] = addr;
+
+	return trace->nr_entries >= trace->max_entries;
+}
+
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+	struct stack_trace_data data;
+	struct stackframe frame;
+
+	data.trace = trace;
+	data.skip = trace->skip;
+
+	if (tsk != current) {
+#ifdef CONFIG_SMP
+		/*
+		 * What guarantees do we have here that 'tsk' is not
+		 * running on another CPU?  For now, ignore it as we
+		 * can't guarantee we won't explode.
+		 */
+		if (trace->nr_entries < trace->max_entries)
+			trace->entries[trace->nr_entries++] = ULONG_MAX;
+		return;
+#else
+		data.no_sched_functions = 1;
+		frame.fp = thread_saved_fp(tsk);
+		frame.sp = thread_saved_sp(tsk);
+		frame.lr = 0;		/* recovered from the stack */
+		frame.pc = thread_saved_pc(tsk);
+#endif
+	} else {
+		register unsigned long current_sp asm ("A0StP");
+
+		data.no_sched_functions = 0;
+		frame.fp = (unsigned long)__builtin_frame_address(0);
+		frame.sp = current_sp;
+		frame.lr = (unsigned long)__builtin_return_address(0);
+		frame.pc = (unsigned long)save_stack_trace_tsk;
+	}
+
+	walk_stackframe(&frame, save_trace, &data);
+	if (trace->nr_entries < trace->max_entries)
+		trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
+
+void save_stack_trace(struct stack_trace *trace)
+{
+	save_stack_trace_tsk(current, trace);
+}
+EXPORT_SYMBOL_GPL(save_stack_trace);
+#endif
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 27/40] metag: various other headers
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (23 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 26/40] metag: Stack unwinding James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 28/40] metag: Perf James Hogan
                   ` (15 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add the remaining metag header files:
 - byteorder.h, swab.h (byte order and swapping)
 - barrier.h, cpu.h. hwthread.h, processor.h (hardware thread related)
 - bug.h, elf.h, gpio.h, linkage.h, resource.h (other)

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/include/asm/barrier.h   |   88 +++++++++++++++
 arch/metag/include/asm/bug.h       |   12 ++
 arch/metag/include/asm/byteorder.h |    1 +
 arch/metag/include/asm/cpu.h       |   14 +++
 arch/metag/include/asm/elf.h       |  131 ++++++++++++++++++++++
 arch/metag/include/asm/gpio.h      |    4 +
 arch/metag/include/asm/hwthread.h  |   44 ++++++++
 arch/metag/include/asm/linkage.h   |    7 ++
 arch/metag/include/asm/processor.h |  209 ++++++++++++++++++++++++++++++++++++
 arch/metag/include/asm/resource.h  |    7 ++
 arch/metag/include/asm/swab.h      |   26 +++++
 11 files changed, 543 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/barrier.h
 create mode 100644 arch/metag/include/asm/bug.h
 create mode 100644 arch/metag/include/asm/byteorder.h
 create mode 100644 arch/metag/include/asm/cpu.h
 create mode 100644 arch/metag/include/asm/elf.h
 create mode 100644 arch/metag/include/asm/gpio.h
 create mode 100644 arch/metag/include/asm/hwthread.h
 create mode 100644 arch/metag/include/asm/linkage.h
 create mode 100644 arch/metag/include/asm/processor.h
 create mode 100644 arch/metag/include/asm/resource.h
 create mode 100644 arch/metag/include/asm/swab.h

diff --git a/arch/metag/include/asm/barrier.h b/arch/metag/include/asm/barrier.h
new file mode 100644
index 0000000..b497a7d
--- /dev/null
+++ b/arch/metag/include/asm/barrier.h
@@ -0,0 +1,88 @@
+#ifndef _ASM_METAG_BARRIER_H
+#define _ASM_METAG_BARRIER_H
+
+#define METAG_ALL_VALUES
+#define METAC_ALL_VALUES
+#include <asm/tbx/machine.inc>
+#include <asm/tbx/metagtbx.h>
+
+#define nop()		__asm__ __volatile__ ("NOP")
+#define mb()		wmb()
+#define rmb()		barrier()
+
+#ifdef CONFIG_META21
+
+/* HTP and above have a system event to fence writes */
+static inline void wr_fence(void)
+{
+	volatile int *flushptr = (volatile int *) LINSYSEVENT_WR_FENCE;
+	barrier();
+	*flushptr = 0;
+}
+
+#else /* CONFIG_META21 */
+
+/*
+ * ATP doesn't have system event to fence writes, so it is necessary to flush
+ * the processor write queues as well as possibly the write combiner (depending
+ * on the page being written).
+ * To ensure the write queues are flushed we do 4 writes to a system event
+ * register (in this case write combiner flush) which will also flush the write
+ * combiner.
+ */
+static inline void wr_fence(void)
+{
+	volatile int *flushptr = (volatile int *) LINSYSEVENT_WR_COMBINE_FLUSH;
+	barrier();
+	*flushptr = 0;
+	*flushptr = 0;
+	*flushptr = 0;
+	*flushptr = 0;
+}
+
+#endif /* !CONFIG_META21 */
+
+static inline void wmb(void)
+{
+	/* flush writes through the write combiner */
+	wr_fence();
+}
+
+#define read_barrier_depends()  do { } while (0)
+
+#ifndef CONFIG_SMP
+#define fence()		do { } while (0)
+#define smp_mb()        barrier()
+#define smp_rmb()       barrier()
+#define smp_wmb()       barrier()
+#else
+
+#ifdef CONFIG_SMP_WRITE_REORDERING
+/*
+ * Write to the atomic memory unlock system event register (command 0). This is
+ * needed before a write to shared memory in a critical section, to prevent
+ * external reordering of writes before the fence on other threads with writes
+ * after the fence on this thread (and to prevent the ensuing cache-memory
+ * incoherence). It is therefore ineffective if used after and on the same
+ * thread as a write.
+ */
+static inline void fence(void)
+{
+	volatile int *flushptr = (volatile int *) LINSYSEVENT_WR_ATOMIC_UNLOCK;
+	barrier();
+	*flushptr = 0;
+}
+#define smp_mb()        fence()
+#define smp_rmb()       fence()
+#define smp_wmb()       barrier()
+#else
+#define fence()		do { } while (0)
+#define smp_mb()        barrier()
+#define smp_rmb()       barrier()
+#define smp_wmb()       barrier()
+#endif
+#endif
+#define smp_read_barrier_depends()     do { } while (0)
+#define set_mb(var, value) do { var = value; smp_mb(); } while (0)
+
+#endif /* _ASM_METAG_BARRIER_H */
diff --git a/arch/metag/include/asm/bug.h b/arch/metag/include/asm/bug.h
new file mode 100644
index 0000000..d04b48c
--- /dev/null
+++ b/arch/metag/include/asm/bug.h
@@ -0,0 +1,12 @@
+#ifndef _ASM_METAG_BUG_H
+#define _ASM_METAG_BUG_H
+
+#include <asm-generic/bug.h>
+
+struct pt_regs;
+
+extern const char *trap_name(int trapno);
+extern void die(const char *str, struct pt_regs *regs, long err,
+		unsigned long addr) __attribute__ ((noreturn));
+
+#endif
diff --git a/arch/metag/include/asm/byteorder.h b/arch/metag/include/asm/byteorder.h
new file mode 100644
index 0000000..9558416
--- /dev/null
+++ b/arch/metag/include/asm/byteorder.h
@@ -0,0 +1 @@
+#include <linux/byteorder/little_endian.h>
diff --git a/arch/metag/include/asm/cpu.h b/arch/metag/include/asm/cpu.h
new file mode 100644
index 0000000..decf129
--- /dev/null
+++ b/arch/metag/include/asm/cpu.h
@@ -0,0 +1,14 @@
+#ifndef _ASM_METAG_CPU_H
+#define _ASM_METAG_CPU_H
+
+#include <linux/percpu.h>
+
+struct cpuinfo_metag {
+	struct cpu cpu;
+#ifdef CONFIG_SMP
+	unsigned long loops_per_jiffy;
+#endif
+};
+
+DECLARE_PER_CPU(struct cpuinfo_metag, cpu_data);
+#endif /* _ASM_METAG_CPU_H */
diff --git a/arch/metag/include/asm/elf.h b/arch/metag/include/asm/elf.h
new file mode 100644
index 0000000..bd4887e
--- /dev/null
+++ b/arch/metag/include/asm/elf.h
@@ -0,0 +1,131 @@
+#ifndef __ASM_META_ELF_H
+#define __ASM_META_ELF_H
+
+#define EM_METAG      174
+
+/* Meta relocations */
+#define R_METAG_HIADDR16                 0
+#define R_METAG_LOADDR16                 1
+#define R_METAG_ADDR32                   2
+#define R_METAG_NONE                     3
+#define R_METAG_RELBRANCH                4
+#define R_METAG_GETSETOFF                5
+
+/* Backward compatability */
+#define R_METAG_REG32OP1                 6
+#define R_METAG_REG32OP2                 7
+#define R_METAG_REG32OP3                 8
+#define R_METAG_REG16OP1                 9
+#define R_METAG_REG16OP2                10
+#define R_METAG_REG16OP3                11
+#define R_METAG_REG32OP4                12
+
+#define R_METAG_HIOG                    13
+#define R_METAG_LOOG                    14
+
+/* GNU */
+#define R_METAG_GNU_VTINHERIT           30
+#define R_METAG_GNU_VTENTRY             31
+
+/* PIC relocations */
+#define R_METAG_HI16_GOTOFF             32
+#define R_METAG_LO16_GOTOFF             33
+#define R_METAG_GETSET_GOTOFF           34
+#define R_METAG_GETSET_GOT              35
+#define R_METAG_HI16_GOTPC              36
+#define R_METAG_LO16_GOTPC              37
+#define R_METAG_HI16_PLT                38
+#define R_METAG_LO16_PLT                39
+#define R_METAG_RELBRANCH_PLT           40
+#define R_METAG_GOTOFF                  41
+#define R_METAG_PLT                     42
+#define R_METAG_COPY                    43
+#define R_METAG_JMP_SLOT                44
+#define R_METAG_RELATIVE                45
+#define R_METAG_GLOB_DAT                46
+
+/*
+ * ELF register definitions.
+ */
+
+#include <asm/ptrace.h>
+#include <asm/user.h>
+
+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 unsigned long 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_METAG)
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS	ELFCLASS32
+#define ELF_DATA	ELFDATA2LSB
+#define ELF_ARCH	EM_METAG
+
+#ifdef __KERNEL__
+
+#include <asm/page.h>
+#include <asm/processor.h>
+
+#define ELF_PLAT_INIT(_r, load_addr)	\
+	do { _r->ctx.AX[0].U0 = 0; } while (0)
+
+#define USE_ELF_CORE_DUMP
+#define CORE_DUMP_USE_REGSET
+#define ELF_EXEC_PAGESIZE	PAGE_SIZE
+
+/* 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         0x08000000UL
+
+#define ELF_CORE_COPY_REGS(_dest, _regs)			\
+	memcpy((char *)&_dest, (char *)_regs, sizeof(struct pt_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) set_personality(PER_LINUX)
+#endif
+
+#define STACK_RND_MASK (0)
+
+#ifdef CONFIG_META_USER_TCM
+
+struct elf32_phdr;
+struct file;
+
+unsigned long __metag_elf_map(struct file *filep, unsigned long addr,
+			      struct elf32_phdr *eppnt, int prot, int type,
+			      unsigned long total_size);
+
+static inline unsigned long metag_elf_map(struct file *filep,
+					  unsigned long addr,
+					  struct elf32_phdr *eppnt, int prot,
+					  int type, unsigned long total_size)
+{
+	return __metag_elf_map(filep, addr, eppnt, prot, type, total_size);
+}
+#define elf_map metag_elf_map
+
+#endif
+
+#endif
diff --git a/arch/metag/include/asm/gpio.h b/arch/metag/include/asm/gpio.h
new file mode 100644
index 0000000..b3799d8
--- /dev/null
+++ b/arch/metag/include/asm/gpio.h
@@ -0,0 +1,4 @@
+#ifndef __LINUX_GPIO_H
+#warning Include linux/gpio.h instead of asm/gpio.h
+#include <linux/gpio.h>
+#endif
diff --git a/arch/metag/include/asm/hwthread.h b/arch/metag/include/asm/hwthread.h
new file mode 100644
index 0000000..f855d03
--- /dev/null
+++ b/arch/metag/include/asm/hwthread.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 Imagination Technologies
+ */
+#ifndef __METAG_HWTHREAD_H
+#define __METAG_HWTHREAD_H
+
+#define METAG_ALL_VALUES
+#define METAC_ALL_VALUES
+#define METAG_TXUXXRX_VALUES
+#include <asm/tbx/machine.inc>
+#include <asm/tbx/metagtbx.h>
+
+#include <linux/bug.h>
+#include <linux/io.h>
+
+#define BAD_HWTHREAD_ID		(0xFFU)
+#define BAD_CPU_ID		(0xFFU)
+
+extern u8 cpu_2_hwthread_id[];
+extern u8 hwthread_id_2_cpu[];
+
+/*
+ * Each hardware thread's Control Unit registers are memory-mapped
+ * and can therefore be accessed by any other hardware thread.
+ *
+ * This helper function returns the memory address where "thread"'s
+ * register "regnum" is mapped.
+ */
+static inline
+void __iomem *__CU_addr(unsigned int thread, unsigned int regnum)
+{
+	unsigned int base, thread_offset, thread_regnum;
+
+	WARN_ON(thread == BAD_HWTHREAD_ID);
+
+	base = T0UCTREG0;	/* Control unit base */
+
+	thread_offset = TnUCTRX_STRIDE * thread;
+	thread_regnum = TXUCTREGn_STRIDE * regnum;
+
+	return (void __iomem *)(base + thread_offset + thread_regnum);
+}
+
+#endif /* __METAG_HWTHREAD_H */
diff --git a/arch/metag/include/asm/linkage.h b/arch/metag/include/asm/linkage.h
new file mode 100644
index 0000000..73bf25b
--- /dev/null
+++ b/arch/metag/include/asm/linkage.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_LINKAGE_H
+#define __ASM_LINKAGE_H
+
+#define __ALIGN .p2align 2
+#define __ALIGN_STR ".p2align 2"
+
+#endif
diff --git a/arch/metag/include/asm/processor.h b/arch/metag/include/asm/processor.h
new file mode 100644
index 0000000..2248c21
--- /dev/null
+++ b/arch/metag/include/asm/processor.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 Imagination Technologies
+ */
+
+#ifndef __ASM_METAG_PROCESSOR_H
+#define __ASM_METAG_PROCESSOR_H
+
+#ifdef __KERNEL__
+
+#include <linux/atomic.h>
+
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/hw_breakpoints.h>
+
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l; })
+
+/* The task stops where the kernel starts */
+#define TASK_SIZE	PAGE_OFFSET
+/* Add an extra page of padding at the top of the stack for the guard page. */
+#define STACK_TOP	(TASK_SIZE - PAGE_SIZE)
+#define STACK_TOP_MAX	STACK_TOP
+
+/* This decides where the kernel will search for a free chunk of vm
+ * space during mmap's.
+ */
+#define TASK_UNMAPPED_BASE	META_MEMORY_BASE
+
+typedef struct {
+	unsigned long seg;
+} mm_segment_t;
+
+#ifdef CONFIG_META_FPU
+struct meta_fpu_context {
+	TBICTXEXTFPU fpstate;
+	union {
+		struct {
+			TBICTXEXTBB4 fx8_15;
+			TBICTXEXTFPACC fpacc;
+		} fx8_15;
+		struct {
+			TBICTXEXTFPACC fpacc;
+			TBICTXEXTBB4 unused;
+		} nofx8_15;
+	} extfpstate;
+	bool needs_restore;
+};
+#else
+struct meta_fpu_context {};
+#endif
+
+#ifdef CONFIG_META_DSP
+struct meta_ext_context {
+	struct {
+		TBIEXTCTX ctx;
+		TBICTXEXTBB8 bb8;
+		TBIDUAL ax[TBICTXEXTAXX_BYTES / sizeof(TBIDUAL)];
+		TBICTXEXTHL2 hl2;
+		TBICTXEXTTDPR ext;
+		TBICTXEXTRP6 rp;
+	} regs;
+
+	/* DSPRAM A and B save areas. */
+	void *ram[2];
+
+	/* ECH encoded size of DSPRAM save areas. */
+	unsigned int ram_sz[2];
+};
+#else
+struct meta_ext_context {};
+#endif
+
+struct thread_struct {
+	PTBICTX kernel_context;
+	/* A copy of the user process Sig.SaveMask. */
+	unsigned int user_flags;
+	struct meta_fpu_context *fpu_context;
+	struct meta_hw_breakpoint *hwbp_context;
+	void __user *tls_ptr;
+	unsigned short int_depth;
+	unsigned short txdefr_failure;
+	struct meta_ext_context *dsp_context;
+};
+
+#define INIT_THREAD  { \
+	NULL,			/* kernel_context */	\
+	0,			/* user_flags */	\
+	NULL,			/* fpu_context */	\
+	NULL,			/* hwbp_context */	\
+	NULL,			/* tls_ptr */		\
+	1,			/* int_depth - we start in kernel */	\
+	0,			/* txdefr_failure */	\
+	NULL,			/* dsp_context */	\
+}
+
+/* Needed to make #define as we are referencing 'current', that is not visible
+ * yet.
+ *
+ * Stack layout is as below.
+
+      argc            argument counter (integer)
+      argv[0]         program name (pointer)
+      argv[1...N]     program args (pointers)
+      argv[argc-1]    end of args (integer)
+      NULL
+      env[0...N]      environment variables (pointers)
+      NULL
+
+ */
+#define start_thread(regs, pc, usp) do {				   \
+	unsigned int *argc = (unsigned int *) bprm->exec;		   \
+	set_fs(USER_DS);						   \
+	current->thread.int_depth = 1;					   \
+	/* Force this process down to user land */			   \
+	regs->ctx.SaveMask = TBICTX_PRIV_BIT;				   \
+	regs->ctx.CurrPC = pc;						   \
+	regs->ctx.AX[0].U0 = usp;					   \
+	regs->ctx.DX[3].U1 = *((int *)argc);			/* argc */ \
+	regs->ctx.DX[3].U0 = (int)((int *)argc + 1);		/* argv */ \
+	regs->ctx.DX[2].U1 = (int)((int *)argc +			   \
+				   regs->ctx.DX[3].U1 + 2);	/* envp */ \
+	regs->ctx.DX[2].U0 = 0;				   /* rtld_fini */ \
+} 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)
+{
+}
+
+#define copy_segments(tsk, mm)		do { } while (0)
+#define release_segments(mm)		do { } while (0)
+
+extern void exit_thread(void);
+
+/*
+ * Return saved PC of a blocked thread.
+ */
+#define	thread_saved_pc(tsk)	\
+	((unsigned long)(tsk)->thread.kernel_context->CurrPC)
+#define thread_saved_sp(tsk)	\
+	((unsigned long)(tsk)->thread.kernel_context->AX[0].U0)
+#define thread_saved_fp(tsk)	\
+	((unsigned long)(tsk)->thread.kernel_context->AX[1].U0)
+
+unsigned long get_wchan(struct task_struct *p);
+
+#define	KSTK_EIP(tsk)	((tsk)->thread.kernel_context->CurrPC)
+#define	KSTK_ESP(tsk)	((tsk)->thread.kernel_context->AX[0].U0)
+
+#define user_stack_pointer(regs)        ((regs)->ctx.AX[0].U0)
+
+int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags);
+
+#define cpu_relax()     barrier()
+
+extern void setup_txprivext(void);
+
+static inline unsigned int hard_processor_id(void)
+{
+	unsigned int id;
+
+	__asm__ __volatile__(
+			    "MOV	%0, TXENABLE\n"
+			    "AND	%0, %0, %1\n"
+			    "LSR	%0, %0, %2\n"
+			    : "=&d" (id)
+			    : "I" (TXENABLE_THREAD_BITS),
+			      "K" (TXENABLE_THREAD_S)
+	);
+
+	return id;
+}
+
+#define OP3_EXIT	0
+
+#define HALT_OK		0
+#define HALT_PANIC	-1
+
+/*
+ * Halt (stop) the hardware thread. This instruction sequence is the
+ * standard way to cause a Meta hardware thread to exit. The exit code
+ * is pushed onto the stack which is interpreted by the debug adapter.
+ */
+static inline void hard_processor_halt(int exit_code)
+{
+	__asm__ __volatile__("MOV	D1Ar1, %0\n"
+			     "MOV	D0Ar6, %1\n"
+			     "MSETL	[A0StP],D0Ar6,D0Ar4,D0Ar2\n"
+			     "1: SWITCH	#0xC30006\n"
+			     "B		1b\n"
+			     : : "r" (exit_code), "K" (OP3_EXIT));
+}
+
+/* Set these hooks to call SoC specific code to restart/halt/power off. */
+extern void (*soc_restart)(char *cmd);
+extern void (*soc_halt)(void);
+
+extern void show_trace(struct task_struct *tsk, unsigned long *sp,
+		       struct pt_regs *regs);
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/arch/metag/include/asm/resource.h b/arch/metag/include/asm/resource.h
new file mode 100644
index 0000000..278b3bc
--- /dev/null
+++ b/arch/metag/include/asm/resource.h
@@ -0,0 +1,7 @@
+#ifndef _METAG_RESOURCE_H
+#define _METAG_RESOURCE_H
+
+#define _STK_LIM_MAX    (1 << 28)
+#include <asm-generic/resource.h>
+
+#endif
diff --git a/arch/metag/include/asm/swab.h b/arch/metag/include/asm/swab.h
new file mode 100644
index 0000000..1076b3a
--- /dev/null
+++ b/arch/metag/include/asm/swab.h
@@ -0,0 +1,26 @@
+#ifndef __ASM_METAG_SWAB_H
+#define __ASM_METAG_SWAB_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <asm-generic/swab.h>
+
+static inline __attribute_const__ __u16 __arch_swab16(__u16 x)
+{
+	return __builtin_metag_bswaps(x);
+}
+#define __arch_swab16 __arch_swab16
+
+static inline __attribute_const__ __u32 __arch_swab32(__u32 x)
+{
+	return __builtin_metag_bswap(x);
+}
+#define __arch_swab32 __arch_swab32
+
+static inline __attribute_const__ __u64 __arch_swab64(__u64 x)
+{
+	return __builtin_metag_bswapll(x);
+}
+#define __arch_swab64 __arch_swab64
+
+#endif /* __ASM_METAG_SWAB_H */
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 28/40] metag: Perf
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (24 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 27/40] metag: various other headers James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 29/40] metag: ftrace support James Hogan
                   ` (14 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add Perf support to Meta.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/Kconfig                  |    1 +
 arch/metag/include/asm/perf_event.h |    4 ++
 arch/metag/kernel/perf_callchain.c  |   96 +++++++++++++++++++++++++++++++++++
 3 files changed, 101 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/perf_event.h
 create mode 100644 arch/metag/kernel/perf_callchain.c

diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index 5141108..c7c84d1 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -9,6 +9,7 @@ config METAG
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK_NODE_MAP
 	select HAVE_ARCH_TRACEHOOK
+	select HAVE_PERF_EVENTS
 	select HAVE_IRQ_WORK
 	select HAVE_KERNEL_GZIP
 	select HAVE_KERNEL_BZIP2
diff --git a/arch/metag/include/asm/perf_event.h b/arch/metag/include/asm/perf_event.h
new file mode 100644
index 0000000..105bbff
--- /dev/null
+++ b/arch/metag/include/asm/perf_event.h
@@ -0,0 +1,4 @@
+#ifndef __ASM_METAG_PERF_EVENT_H
+#define __ASM_METAG_PERF_EVENT_H
+
+#endif /* __ASM_METAG_PERF_EVENT_H */
diff --git a/arch/metag/kernel/perf_callchain.c b/arch/metag/kernel/perf_callchain.c
new file mode 100644
index 0000000..3156334
--- /dev/null
+++ b/arch/metag/kernel/perf_callchain.c
@@ -0,0 +1,96 @@
+/*
+ * Perf callchain handling code.
+ *
+ *   Based on the ARM perf implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/perf_event.h>
+#include <linux/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/stacktrace.h>
+
+static bool is_valid_call(unsigned long calladdr)
+{
+	unsigned int callinsn;
+
+	/* Check the possible return address is aligned. */
+	if (!(calladdr & 0x3)) {
+		if (!get_user(callinsn, (unsigned int *)calladdr)) {
+			/* Check for CALLR or SWAP PC,D1RtP. */
+			if ((callinsn & 0xff000000) == 0xab000000 ||
+			    callinsn == 0xa3200aa0)
+				return true;
+		}
+	}
+	return false;
+}
+
+static struct metag_frame __user *
+user_backtrace(struct metag_frame __user *user_frame,
+	       struct perf_callchain_entry *entry)
+{
+	struct metag_frame frame;
+	unsigned long calladdr;
+
+	/* We cannot rely on having frame pointers in user code. */
+	while (1) {
+		/* Also check accessibility of one struct frame beyond */
+		if (!access_ok(VERIFY_READ, user_frame, sizeof(frame)))
+			return 0;
+		if (__copy_from_user_inatomic(&frame, user_frame,
+					      sizeof(frame)))
+			return 0;
+
+		--user_frame;
+
+		calladdr = frame.lr - 4;
+		if (is_valid_call(calladdr)) {
+			perf_callchain_store(entry, calladdr);
+			return user_frame;
+		}
+	}
+
+	return 0;
+}
+
+void
+perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
+{
+	unsigned long sp = regs->ctx.AX[0].U0;
+	struct metag_frame __user *frame;
+
+	frame = (struct metag_frame __user *)sp;
+
+	--frame;
+
+	while ((entry->nr < PERF_MAX_STACK_DEPTH) && frame)
+		frame = user_backtrace(frame, entry);
+}
+
+/*
+ * Gets called by walk_stackframe() for every stackframe. This will be called
+ * whist unwinding the stackframe and is like a subroutine return so we use
+ * the PC.
+ */
+static int
+callchain_trace(struct stackframe *fr,
+		void *data)
+{
+	struct perf_callchain_entry *entry = data;
+	perf_callchain_store(entry, fr->pc);
+	return 0;
+}
+
+void
+perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
+{
+	struct stackframe fr;
+
+	fr.fp = regs->ctx.AX[1].U0;
+	fr.sp = regs->ctx.AX[0].U0;
+	fr.lr = regs->ctx.DX[4].U1;
+	fr.pc = regs->ctx.CurrPC;
+	walk_stackframe(&fr, callchain_trace, entry);
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 29/40] metag: ftrace support
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (25 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 28/40] metag: Perf James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 30/40] scripts/checkstack.pl: Add metag support James Hogan
                   ` (13 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add ftrace support for metag.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/Kconfig              |    4 +
 arch/metag/include/asm/Kbuild   |    1 -
 arch/metag/include/asm/ftrace.h |   23 +++++++
 arch/metag/kernel/ftrace.c      |  127 +++++++++++++++++++++++++++++++++++++++
 arch/metag/kernel/ftrace_stub.S |   76 +++++++++++++++++++++++
 arch/metag/kernel/metag_ksyms.c |    1 -
 scripts/recordmcount.pl         |   12 ++++
 7 files changed, 242 insertions(+), 2 deletions(-)
 create mode 100644 arch/metag/include/asm/ftrace.h
 create mode 100644 arch/metag/kernel/ftrace.c
 create mode 100644 arch/metag/kernel/ftrace_stub.S

diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index c7c84d1..a0f1a46 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -8,6 +8,10 @@ config METAG
 	select HAVE_64BIT_ALIGNED_STRUCT
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK_NODE_MAP
+	select HAVE_FUNCTION_TRACER
+	select HAVE_FUNCTION_TRACE_MCOUNT_TEST
+	select HAVE_DYNAMIC_FTRACE
+	select HAVE_FTRACE_MCOUNT_RECORD
 	select HAVE_ARCH_TRACEHOOK
 	select HAVE_PERF_EVENTS
 	select HAVE_IRQ_WORK
diff --git a/arch/metag/include/asm/Kbuild b/arch/metag/include/asm/Kbuild
index 1678d85..5939502 100644
--- a/arch/metag/include/asm/Kbuild
+++ b/arch/metag/include/asm/Kbuild
@@ -16,7 +16,6 @@ generic-y += errno.h
 generic-y += exec.h
 generic-y += fb.h
 generic-y += fcntl.h
-generic-y += ftrace.h
 generic-y += futex.h
 generic-y += hardirq.h
 generic-y += hw_irq.h
diff --git a/arch/metag/include/asm/ftrace.h b/arch/metag/include/asm/ftrace.h
new file mode 100644
index 0000000..2901f0f
--- /dev/null
+++ b/arch/metag/include/asm/ftrace.h
@@ -0,0 +1,23 @@
+#ifndef _ASM_METAG_FTRACE
+#define _ASM_METAG_FTRACE
+
+#ifdef CONFIG_FUNCTION_TRACER
+#define MCOUNT_INSN_SIZE	8 /* sizeof mcount call */
+
+#ifndef __ASSEMBLY__
+extern void mcount_wrapper(void);
+#define MCOUNT_ADDR		((long)(mcount_wrapper))
+
+static inline unsigned long ftrace_call_adjust(unsigned long addr)
+{
+	return addr;
+}
+
+struct dyn_arch_ftrace {
+	/* No extra data needed on metag */
+};
+#endif /* __ASSEMBLY__ */
+
+#endif /* CONFIG_FUNCTION_TRACER */
+
+#endif /* _ASM_METAG_FTRACE */
diff --git a/arch/metag/kernel/ftrace.c b/arch/metag/kernel/ftrace.c
new file mode 100644
index 0000000..8cbeb53
--- /dev/null
+++ b/arch/metag/kernel/ftrace.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008 Imagination Technologies Ltd.
+ * Licensed under the GPL
+ *
+ * Dynamic ftrace support.
+ */
+
+#include <linux/ftrace.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+
+#include <asm/cacheflush.h>
+
+#define D04_MOVT_TEMPLATE	0x02200005
+#define D04_CALL_TEMPLATE	0xAC200005
+#define D1RTP_MOVT_TEMPLATE	0x03200005
+#define D1RTP_CALL_TEMPLATE	0xAC200006
+
+static const unsigned long NOP[2] = {0xa0fffffe, 0xa0fffffe};
+static unsigned long movt_and_call_insn[2];
+
+static unsigned char *ftrace_nop_replace(void)
+{
+	return (char *)&NOP[0];
+}
+
+static unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr)
+{
+	unsigned long hi16, low16;
+
+	hi16 = (addr & 0xffff0000) >> 13;
+	low16 = (addr & 0x0000ffff) << 3;
+
+	/*
+	 * The compiler makes the call to mcount_wrapper()
+	 * (Meta's wrapper around mcount()) through the register
+	 * D0.4. So whenever we're patching one of those compiler-generated
+	 * calls we also need to go through D0.4. Otherwise use D1RtP.
+	 */
+	if (pc == (unsigned long)&ftrace_call) {
+		writel(D1RTP_MOVT_TEMPLATE | hi16, &movt_and_call_insn[0]);
+		writel(D1RTP_CALL_TEMPLATE | low16, &movt_and_call_insn[1]);
+	} else {
+		writel(D04_MOVT_TEMPLATE | hi16, &movt_and_call_insn[0]);
+		writel(D04_CALL_TEMPLATE | low16, &movt_and_call_insn[1]);
+	}
+
+	return (unsigned char *)&movt_and_call_insn[0];
+}
+
+static int ftrace_modify_code(unsigned long pc, unsigned char *old_code,
+			      unsigned char *new_code)
+{
+	unsigned char replaced[MCOUNT_INSN_SIZE];
+
+	/*
+	 * Note: Due to modules and __init, code can
+	 *  disappear and change, we need to protect against faulting
+	 *  as well as code changing.
+	 *
+	 * No real locking needed, this code is run through
+	 * kstop_machine.
+	 */
+
+	/* read the text we want to modify */
+	if (probe_kernel_read(replaced, (void *)pc, MCOUNT_INSN_SIZE))
+		return -EFAULT;
+
+	/* Make sure it is what we expect it to be */
+	if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
+		return -EINVAL;
+
+	/* replace the text with the new text */
+	if (probe_kernel_write((void *)pc, new_code, MCOUNT_INSN_SIZE))
+		return -EPERM;
+
+	flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
+
+	return 0;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+	int ret;
+	unsigned long pc;
+	unsigned char old[MCOUNT_INSN_SIZE], *new;
+
+	pc = (unsigned long)&ftrace_call;
+	memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
+	new = ftrace_call_replace(pc, (unsigned long)func);
+	ret = ftrace_modify_code(pc, old, new);
+
+	return ret;
+}
+
+int ftrace_make_nop(struct module *mod,
+		    struct dyn_ftrace *rec, unsigned long addr)
+{
+	unsigned char *new, *old;
+	unsigned long ip = rec->ip;
+
+	old = ftrace_call_replace(ip, addr);
+	new = ftrace_nop_replace();
+
+	return ftrace_modify_code(ip, old, new);
+}
+
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+	unsigned char *new, *old;
+	unsigned long ip = rec->ip;
+
+	old = ftrace_nop_replace();
+	new = ftrace_call_replace(ip, addr);
+
+	return ftrace_modify_code(ip, old, new);
+}
+
+/* run from kstop_machine */
+int __init ftrace_dyn_arch_init(void *data)
+{
+	/* The return code is returned via data */
+	writel(0, data);
+
+	return 0;
+}
diff --git a/arch/metag/kernel/ftrace_stub.S b/arch/metag/kernel/ftrace_stub.S
new file mode 100644
index 0000000..e70bff7
--- /dev/null
+++ b/arch/metag/kernel/ftrace_stub.S
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 Imagination Technologies Ltd.
+ * Licensed under the GPL
+ *
+ */
+
+#include <asm/ftrace.h>
+
+	.text
+#ifdef CONFIG_DYNAMIC_FTRACE
+	.global	_mcount_wrapper
+	.type	_mcount_wrapper,function
+_mcount_wrapper:
+	MOV	PC,D0.4
+
+	.global _ftrace_caller
+	.type	_ftrace_caller,function
+_ftrace_caller:
+	MOVT    D0Re0,#HI(_function_trace_stop)
+	ADD	D0Re0,D0Re0,#LO(_function_trace_stop)
+	GETD	D0Re0,[D0Re0]
+	CMP	D0Re0,#0
+	BEQ	$Lcall_stub
+	MOV	PC,D0.4
+$Lcall_stub:
+	MSETL   [A0StP], D0Ar6, D0Ar4, D0Ar2, D0.4
+	MOV     D1Ar1, D0.4
+	MOV     D0Ar2, D1RtP
+	SUB	D1Ar1,D1Ar1,#MCOUNT_INSN_SIZE
+
+	.global _ftrace_call
+_ftrace_call:
+	MOVT	D1RtP,#HI(_ftrace_stub)
+	CALL	D1RtP,#LO(_ftrace_stub)
+	GETL    D0.4,  D1RtP, [A0StP++#(-8)]
+	GETL    D0Ar2, D1Ar1, [A0StP++#(-8)]
+	GETL    D0Ar4, D1Ar3, [A0StP++#(-8)]
+	GETL    D0Ar6, D1Ar5, [A0StP++#(-8)]
+	MOV     PC, D0.4
+#else
+
+	.global	_mcount_wrapper
+	.type	_mcount_wrapper,function
+_mcount_wrapper:
+	MOVT    D0Re0,#HI(_function_trace_stop)
+	ADD	D0Re0,D0Re0,#LO(_function_trace_stop)
+	GETD	D0Re0,[D0Re0]
+	CMP	D0Re0,#0
+	BEQ	$Lcall_mcount
+	MOV	PC,D0.4
+$Lcall_mcount:
+	MSETL   [A0StP], D0Ar6, D0Ar4, D0Ar2, D0.4
+	MOV     D1Ar1, D0.4
+	MOV     D0Ar2, D1RtP
+	MOVT    D0Re0,#HI(_ftrace_trace_function)
+	ADD	D0Re0,D0Re0,#LO(_ftrace_trace_function)
+	GET	D1Ar3,[D0Re0]
+	MOVT	D1Re0,#HI(_ftrace_stub)
+	ADD	D1Re0,D1Re0,#LO(_ftrace_stub)
+	CMP	D1Ar3,D1Re0
+	BEQ	$Ltrace_exit
+	MOV	D1RtP,D1Ar3
+	SUB	D1Ar1,D1Ar1,#MCOUNT_INSN_SIZE
+	SWAP	PC,D1RtP
+$Ltrace_exit:
+	GETL    D0.4,  D1RtP, [A0StP++#(-8)]
+	GETL    D0Ar2, D1Ar1, [A0StP++#(-8)]
+	GETL    D0Ar4, D1Ar3, [A0StP++#(-8)]
+	GETL    D0Ar6, D1Ar5, [A0StP++#(-8)]
+	MOV     PC, D0.4
+
+#endif	/* CONFIG_DYNAMIC_FTRACE */
+
+	.global _ftrace_stub
+_ftrace_stub:
+	MOV 	PC,D1RtP
diff --git a/arch/metag/kernel/metag_ksyms.c b/arch/metag/kernel/metag_ksyms.c
index 7b64076..bb167cf 100644
--- a/arch/metag/kernel/metag_ksyms.c
+++ b/arch/metag/kernel/metag_ksyms.c
@@ -76,6 +76,5 @@ EXPORT_SYMBOL(memset);
 EXPORT_SYMBOL(memmove);
 
 #ifdef CONFIG_FUNCTION_TRACER
-EXPORT_SYMBOL(mcount);
 EXPORT_SYMBOL(mcount_wrapper);
 #endif
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index 858966a..b33446c 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -306,6 +306,18 @@ if ($arch eq "x86_64") {
     $ld .= " -m elf64_sparc";
     $cc .= " -m64";
     $objcopy .= " -O elf64-sparc";
+
+} elsif ($arch eq "metag") {
+    $function_regex = "^([0-9a-fA-F]+)\\s+<([^\$].*?)>:";
+    $mcount_regex = "^\\s*([0-9a-fA-F]+): R_METAG_HIADDR16\\s+_mcount_wrapper";
+    $alignment = 2;
+
+    # force flags for this arch
+    $ld .= " -m elf32metag";
+    $objdump .= " -M metag";
+    $objcopy .= " -O elf32-metag";
+    $cc .= " -m32";
+
 } elsif ($arch eq "mips") {
     # To enable module support, we need to enable the -mlong-calls option
     # of gcc for module, after using this option, we can not get the real
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 30/40] scripts/checkstack.pl: Add metag support
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (26 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 29/40] metag: ftrace support James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 31/40] char: don't build rtc or genrtc on METAG James Hogan
                   ` (12 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Adapt checkstack.pl so that it works for metag.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 scripts/checkstack.pl |    8 ++++++--
 1 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/scripts/checkstack.pl b/scripts/checkstack.pl
index 17e3843..544aa56 100755
--- a/scripts/checkstack.pl
+++ b/scripts/checkstack.pl
@@ -34,7 +34,7 @@ use strict;
 # $1 (first bracket) matches the dynamic amount of the stack growth
 #
 # use anything else and feel the pain ;)
-my (@stack, $re, $dre, $x, $xs);
+my (@stack, $re, $dre, $x, $xs, $funcre);
 {
 	my $arch = shift;
 	if ($arch eq "") {
@@ -44,6 +44,7 @@ my (@stack, $re, $dre, $x, $xs);
 
 	$x	= "[0-9a-f]";	# hex character
 	$xs	= "[0-9a-f ]";	# hex character or space
+	$funcre = qr/^$x* <(.*)>:$/;
 	if ($arch eq 'arm') {
 		#c0008ffc:	e24dd064	sub	sp, sp, #100	; 0x64
 		$re = qr/.*sub.*sp, sp, #(([0-9]{2}|[3-9])[0-9]{2})/o;
@@ -66,6 +67,10 @@ my (@stack, $re, $dre, $x, $xs);
 		#    2b6c:       4e56 fb70       linkw %fp,#-1168
 		#  1df770:       defc ffe4       addaw #-28,%sp
 		$re = qr/.*(?:linkw %fp,|addaw )#-([0-9]{1,4})(?:,%sp)?$/o;
+	} elsif ($arch eq 'metag') {
+		#400026fc:       40 00 00 82     ADD       A0StP,A0StP,#0x8
+		$re = qr/.*ADD.*A0StP,A0StP,\#(0x$x{1,8})/o;
+		$funcre = qr/^$x* <[^\$](.*)>:$/;
 	} elsif ($arch eq 'mips64') {
 		#8800402c:       67bdfff0        daddiu  sp,sp,-16
 		$re = qr/.*daddiu.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o;
@@ -109,7 +114,6 @@ my (@stack, $re, $dre, $x, $xs);
 #
 # main()
 #
-my $funcre = qr/^$x* <(.*)>:$/;
 my ($func, $file, $lastslash);
 
 while (my $line = <STDIN>) {
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 31/40] char: don't build rtc or genrtc on METAG
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (27 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 30/40] scripts/checkstack.pl: Add metag support James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-11-01 15:39   ` Greg KH
  2012-10-31 16:14 ` [RFC PATCH v1 32/40] i8042: don't build " James Hogan
                   ` (11 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

The metag architecture doesn't have a PC RTC or implement the RTC
interface that genrtc expects, so add METAG to the list of architectures
not to build RTC or GEN_RTC on.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 drivers/char/Kconfig |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ea6f632..09b64a8 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -283,7 +283,8 @@ if RTC_LIB=n
 config RTC
 	tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)"
 	depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV \
-			&& !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN && !UML
+			&& !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN \
+			&& !UML && !METAG
 	---help---
 	  If you say Y here and create a character special file /dev/rtc with
 	  major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -331,7 +332,7 @@ config JS_RTC
 
 config GEN_RTC
 	tristate "Generic /dev/rtc emulation"
-	depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32 && !BLACKFIN && !UML
+	depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32 && !BLACKFIN && !UML && !METAG
 	---help---
 	  If you say Y here and create a character special file /dev/rtc with
 	  major number 10 and minor number 135 using mknod ("man mknod"), you
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 32/40] i8042: don't build on METAG
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (28 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 31/40] char: don't build rtc or genrtc on METAG James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-11-09 14:28   ` Arnd Bergmann
  2012-10-31 16:14 ` [RFC PATCH v1 33/40] parport: " James Hogan
                   ` (10 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

The metag architecture port doesn't support the i8042 keyboard
controller, so add METAG to the list of architectures not to build it's
driver on.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 drivers/input/serio/Kconfig |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 55f2c22..60b2a09 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -22,7 +22,7 @@ config SERIO_I8042
 	tristate "i8042 PC Keyboard controller" if EXPERT || !X86
 	default y
 	depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
-		   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
+		   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !METAG
 	help
 	  i8042 is the chip over which the standard AT keyboard and PS/2
 	  mouse are connected to the computer. If you use these devices,
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 33/40] parport: don't build on METAG
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (29 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 32/40] i8042: don't build " James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 34/40] musb: don't redefine {read,write}s{l,w,b} on metag James Hogan
                   ` (9 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

The metag architecture port doesn't support PC parallel ports, so add
METAG to the list of architectures not to build it's driver on.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 drivers/parport/Kconfig |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig
index d92185a..474facb 100644
--- a/drivers/parport/Kconfig
+++ b/drivers/parport/Kconfig
@@ -36,7 +36,7 @@ if PARPORT
 config PARPORT_PC
 	tristate "PC-style hardware"
 	depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && \
-		(!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN
+		(!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN && !METAG
 	---help---
 	  You should say Y here if you have a PC-style parallel port. All
 	  IBM PC compatible computers and some Alphas have PC-style
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 34/40] musb: don't redefine {read,write}s{l,w,b} on metag
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (30 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 33/40] parport: " James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 35/40] vga console: don't build on METAG James Hogan
                   ` (8 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

musb_io.h redefines {read,write}s{l,w,b} functions if the architecture
doesn't match in a particular set. The metag architecture already
defines them which causes compiler errors, so add METAG to the set of
architectures to exclude.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 drivers/usb/musb/musb_io.h |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h
index f7c1c8e..8914872 100644
--- a/drivers/usb/musb/musb_io.h
+++ b/drivers/usb/musb/musb_io.h
@@ -40,7 +40,8 @@
 #if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \
 	&& !defined(CONFIG_AVR32) && !defined(CONFIG_PPC32) \
 	&& !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN) \
-	&& !defined(CONFIG_MIPS) && !defined(CONFIG_M68K)
+	&& !defined(CONFIG_MIPS) && !defined(CONFIG_M68K) \
+	&& !defined(CONFIG_METAG)
 static inline void readsl(const void __iomem *addr, void *buf, int len)
 	{ insl((unsigned long)addr, buf, len); }
 static inline void readsw(const void __iomem *addr, void *buf, int len)
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 35/40] vga console: don't build on METAG
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (31 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 34/40] musb: don't redefine {read,write}s{l,w,b} on metag James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 36/40] metag: OProfile James Hogan
                   ` (7 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

The metag architecture doesn't support VGA so add METAG to the list of
architectures not to build support on.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 drivers/video/console/Kconfig |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index e2c96d0..3ee55e1 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -6,7 +6,7 @@ menu "Console display driver support"
 
 config VGA_CONSOLE
 	bool "VGA text console" if EXPERT || !X86
-	depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER)
+	depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) && !METAG
 	default y
 	help
 	  Saying Y here will allow you to use Linux in text mode through a
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 36/40] metag: OProfile
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (32 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 35/40] vga console: don't build on METAG James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 37/40] metag: Various sysfs drivers James Hogan
                   ` (6 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add oprofile support for metag.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/Kconfig                    |    1 +
 arch/metag/Makefile                   |    2 +
 arch/metag/oprofile/Makefile          |   11 ++
 arch/metag/oprofile/backtrace.c       |  134 ++++++++++++++++++
 arch/metag/oprofile/backtrace.h       |    6 +
 arch/metag/oprofile/op_model_meta12.c |  244 +++++++++++++++++++++++++++++++++
 6 files changed, 398 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/oprofile/Makefile
 create mode 100644 arch/metag/oprofile/backtrace.c
 create mode 100644 arch/metag/oprofile/backtrace.h
 create mode 100644 arch/metag/oprofile/op_model_meta12.c

diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index a0f1a46..74cc946 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -8,6 +8,7 @@ config METAG
 	select HAVE_64BIT_ALIGNED_STRUCT
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK_NODE_MAP
+	select HAVE_OPROFILE
 	select HAVE_FUNCTION_TRACER
 	select HAVE_FUNCTION_TRACE_MCOUNT_TEST
 	select HAVE_DYNAMIC_FTRACE
diff --git a/arch/metag/Makefile b/arch/metag/Makefile
index aae38f6..1e652b4 100644
--- a/arch/metag/Makefile
+++ b/arch/metag/Makefile
@@ -53,6 +53,8 @@ endif
 libs-y 					+= arch/metag/lib/ \
 					   arch/metag/tbx/
 
+drivers-$(CONFIG_OPROFILE)      	+= arch/metag/oprofile/
+
 cflags-$(CONFIG_META_FUNCTION_TRACE) +=  -mhwtrace-leaf -mhwtrace-retpc
 
 cflags-$(CONFIG_META21) += -mextensions=bex
diff --git a/arch/metag/oprofile/Makefile b/arch/metag/oprofile/Makefile
new file mode 100644
index 0000000..9e06d5c
--- /dev/null
+++ b/arch/metag/oprofile/Makefile
@@ -0,0 +1,11 @@
+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) op_model_meta12.o backtrace.o
+
+ccflags-y += -Werror
diff --git a/arch/metag/oprofile/backtrace.c b/arch/metag/oprofile/backtrace.c
new file mode 100644
index 0000000..0ae7489
--- /dev/null
+++ b/arch/metag/oprofile/backtrace.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 Imagination Technologies Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include "backtrace.h"
+
+#ifdef CONFIG_FRAME_POINTER
+
+#ifdef CONFIG_KALLSYMS
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+
+static unsigned long tbi_boing_addr;
+static unsigned long tbi_boing_size;
+#endif
+
+static void user_backtrace_fp(unsigned long __user *fp, unsigned int depth)
+{
+	while (depth-- && access_ok(VERIFY_READ, fp, 8)) {
+		unsigned long addr;
+		unsigned long __user *fpnew;
+		if (__copy_from_user_inatomic(&addr, fp + 1, sizeof(addr)))
+			break;
+		addr -= 4;
+
+		oprofile_add_trace(addr);
+
+		/* stack grows up, so frame pointers must decrease */
+		if (__copy_from_user_inatomic(&fpnew, fp + 0, sizeof(fpnew)))
+			break;
+		if (fpnew > fp)
+			break;
+		fp = fpnew;
+	}
+}
+
+static void kernel_backtrace_fp(unsigned long *fp, unsigned long *stack,
+				unsigned int depth)
+{
+#ifdef CONFIG_KALLSYMS
+	/* We need to know where TBIBoingVec is and it's size */
+	if (!tbi_boing_addr) {
+		unsigned long size;
+		unsigned long offset;
+		char modname[MODULE_NAME_LEN];
+		char name[KSYM_NAME_LEN];
+		tbi_boing_addr = kallsyms_lookup_name("___TBIBoingVec");
+		if (!tbi_boing_addr)
+			tbi_boing_addr = 1;
+		else if (!lookup_symbol_attrs(tbi_boing_addr, &size,
+						&offset, modname, name))
+			tbi_boing_size = size;
+	}
+#endif
+	/* detect when the frame pointer has been used for other purposes and
+	 * doesn't point to the stack (it may point completely elsewhere which
+	 * kstack_end may not detect).
+	 */
+	while (depth-- && fp >= stack && fp + 8 <= stack + THREAD_SIZE) {
+		unsigned long addr;
+		unsigned long *fpnew;
+
+		addr = fp[1] - 4;
+		if (!__kernel_text_address(addr))
+			break;
+
+		oprofile_add_trace(addr);
+
+		/* stack grows up, so frame pointers must decrease */
+		fpnew = (unsigned long *)fp[0];
+		if (fpnew > fp)
+			break;
+		fp = fpnew;
+
+#ifdef CONFIG_KALLSYMS
+		/* If we've reached TBIBoingVec then we're at an interrupt
+		 * entry point or a syscall entry point. The frame pointer
+		 * points to a pt_regs which can be used to continue tracing on
+		 * the other side of the boing.
+		 */
+		if (tbi_boing_size && addr >= tbi_boing_addr &&
+				addr < tbi_boing_addr + tbi_boing_size) {
+			struct pt_regs *regs = (struct pt_regs *)fp;
+			/* OProfile doesn't understand backtracing into
+			 * userland.
+			 */
+			if (!user_mode(regs) && --depth) {
+				oprofile_add_trace(regs->ctx.CurrPC);
+				metag_backtrace(regs, depth);
+			}
+			break;
+		}
+#endif
+	}
+}
+#else
+static void kernel_backtrace_sp(unsigned long *sp, unsigned int depth)
+{
+	while (!kstack_end(sp)) {
+		unsigned long addr = *sp--;
+
+		if (!__kernel_text_address(addr - 4))
+			continue;
+		if (!depth--)
+			break;
+		oprofile_add_trace(addr);
+	}
+}
+#endif
+
+void metag_backtrace(struct pt_regs * const regs, unsigned int depth)
+{
+#ifdef CONFIG_FRAME_POINTER
+	unsigned long *fp = (unsigned long *)regs->ctx.AX[1].U0;
+	if (user_mode(regs))
+		user_backtrace_fp((unsigned long __user __force *)fp, depth);
+	else
+		kernel_backtrace_fp(fp, task_stack_page(current), depth);
+#else
+	if (!user_mode(regs)) {
+		unsigned long *sp = (unsigned long *)regs->ctx.AX[0].U0;
+		kernel_backtrace_sp(sp, depth);
+	}
+#endif
+}
diff --git a/arch/metag/oprofile/backtrace.h b/arch/metag/oprofile/backtrace.h
new file mode 100644
index 0000000..c0fcc42
--- /dev/null
+++ b/arch/metag/oprofile/backtrace.h
@@ -0,0 +1,6 @@
+#ifndef _METAG_OPROFILE_BACKTRACE_H
+#define _METAG_OPROFILE_BACKTRACE_H
+
+void metag_backtrace(struct pt_regs * const regs, unsigned int depth);
+
+#endif
diff --git a/arch/metag/oprofile/op_model_meta12.c b/arch/metag/oprofile/op_model_meta12.c
new file mode 100644
index 0000000..3c0c963
--- /dev/null
+++ b/arch/metag/oprofile/op_model_meta12.c
@@ -0,0 +1,244 @@
+/*
+ *  Meta version derived from arch/sh/oprofile/op_model_sh7750.c
+ *    Copyright (C) 2008 Imagination Technologies Ltd.
+ *
+ * arch/sh/oprofile/op_model_sh7750.c
+ *
+ * OProfile support for SH7750/SH7750S Performance Counters
+ *
+ * Copyright (C) 2003, 2004  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/oprofile.h>
+#include <linux/profile.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#define METAC_PERF_VALUES
+#include <asm/tbx/machine.inc>
+
+#include "backtrace.h"
+
+/*
+ * Meta has 2 perf counters
+ */
+#define NR_CNTRS	2
+
+struct op_counter_config {
+	unsigned long enabled;
+	unsigned long event;
+	unsigned long count;
+	unsigned long unit_mask;
+
+	/* Dummy values for userspace tool compliance */
+	unsigned long kernel;
+	unsigned long user;
+};
+
+static struct op_counter_config ctr[NR_CNTRS];
+
+static u32 meta_read_counter(int counter)
+{
+	u32 val = readl(counter ? PERF_COUNT0 : PERF_COUNT1);
+	return val;
+}
+
+static void meta_write_counter(int counter, u32 val)
+{
+	writel(val, counter ? PERF_COUNT0 : PERF_COUNT1);
+}
+
+/*
+ * Unfortunately we don't have a native exception or interrupt for counter
+ * overflow.
+ *
+ * OProfile on the other hand likes to have samples taken periodically, so
+ * for now we just piggyback the timer interrupt to get the expected
+ * behavior.
+ */
+
+static int meta_timer_notify(struct pt_regs *regs)
+{
+	int i;
+	u32 val, total_val, sub_val;
+	u32 enabled_threads;
+
+	for (i = 0; i < NR_CNTRS; i++) {
+		if (!ctr[i].enabled)
+			continue;
+
+		/* Disable performance monitoring. */
+		enabled_threads = meta_read_counter(i);
+		meta_write_counter(i, 0);
+
+		sub_val = total_val = val = enabled_threads & PERF_COUNT_BITS;
+
+		if (val >= ctr[i].count) {
+			while (val > ctr[i].count) {
+				oprofile_add_sample(regs, i);
+				val -= ctr[i].count;
+			}
+			/* val may be < ctr[i].count but > 0 */
+			sub_val -= val;
+			total_val -= sub_val;
+		}
+
+		/* Enable performance monitoring. */
+		enabled_threads &= (PERF_CTRL_BITS | PERF_THREAD_BITS);
+		enabled_threads = enabled_threads | total_val;
+		meta_write_counter(i, enabled_threads);
+	}
+
+	return 0;
+}
+
+/*
+ * Files will be in a path like:
+ *
+ *  /<oprofilefs mount point>/<counter number>/<file>
+ *
+ * So when dealing with <file>, we look to the parent dentry for the counter
+ * number.
+ */
+static inline int to_counter(struct file *file)
+{
+	long val;
+	const unsigned char *name = file->f_path.dentry->d_parent->d_name.name;
+
+	if (kstrtol(name, 10, &val))
+		return 0;
+
+	return val;
+}
+
+static ssize_t meta_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos, int mask, int shift)
+{
+	int counter = to_counter(file);
+	unsigned long write_val;
+	u32 read_val;
+
+	if (oprofilefs_ulong_from_user(&write_val, buf, count))
+		return -EFAULT;
+
+	read_val = meta_read_counter(counter) & ~mask;
+	write_val <<= shift;
+
+	write_val = read_val | (write_val & mask);
+	meta_write_counter(counter, write_val);
+
+	return count;
+}
+
+/*
+ * These functions handle turning perfomance counters on for particular
+ * threads by writing to files.
+ */
+static ssize_t meta_read_thread(struct file *file, char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	int counter = to_counter(file);
+	u32 val = meta_read_counter(counter);
+
+	val &= PERF_THREAD_BITS;
+	val >>= PERF_THREAD_S;
+
+	return oprofilefs_ulong_to_user((unsigned long)val, buf, count, ppos);
+}
+
+static ssize_t meta_write_thread(struct file *file, const char __user *buf,
+				  size_t count, loff_t *ppos)
+{
+	meta_write(file, buf, count, ppos, PERF_THREAD_BITS, PERF_THREAD_S);
+	return count;
+}
+
+static const struct file_operations thread_fops = {
+	.read		= meta_read_thread,
+	.write		= meta_write_thread,
+};
+
+static int meta_perf_counter_create_files(struct super_block *sb,
+					  struct dentry *root)
+{
+	int i;
+
+	for (i = 0; i < NR_CNTRS; i++) {
+		struct dentry *dir;
+		char buf[4];
+
+		snprintf(buf, sizeof(buf), "%d", i);
+		dir = oprofilefs_mkdir(sb, root, buf);
+
+		oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+		oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+		oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+		oprofilefs_create_file(sb, dir, "unit_mask", &thread_fops);
+
+		/* Dummy entries */
+		oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+		oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+	}
+
+	return 0;
+}
+
+static int meta_perf_counter_start(void)
+{
+	int i;
+	u32 event, read_val;
+
+	for (i = 0; i < NR_CNTRS; i++) {
+		if (!ctr[i].enabled)
+			continue;
+
+		event = ctr[i].event << PERF_CTRL_S;
+		read_val = meta_read_counter(i) & ~PERF_CTRL_BITS;
+		meta_write_counter(i, read_val | event);
+	}
+
+	return register_timer_hook(meta_timer_notify);
+}
+
+static void meta_perf_counter_stop(void)
+{
+	u32 val;
+
+	val = meta_read_counter(0) & ~PERF_THREAD_BITS;
+	meta_write_counter(0, val);
+
+	val = meta_read_counter(1) & ~PERF_THREAD_BITS;
+	meta_write_counter(1, val);
+
+	unregister_timer_hook(meta_timer_notify);
+}
+
+int __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+	ops->cpu_type = "metag";
+	ops->create_files = meta_perf_counter_create_files;
+	ops->start = meta_perf_counter_start;
+	ops->stop = meta_perf_counter_stop;
+	ops->backtrace = metag_backtrace;
+
+	pr_info("oprofile: using %s performance monitoring.\n", ops->cpu_type);
+
+	/* Clear the counters. */
+	meta_write_counter(0, 0);
+	meta_write_counter(1, 0);
+
+	return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+}
+
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 37/40] metag: Various sysfs drivers
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (33 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 36/40] metag: OProfile James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 19:30   ` Greg KH
  2012-10-31 16:14 ` [RFC PATCH v1 38/40] metag: add JTAG Debug Adapter (DA) support James Hogan
                   ` (5 subsequent siblings)
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add various Meta specific sysfs drivers.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/Kconfig                          |    2 +
 arch/metag/include/asm/core-sysfs.h         |    9 +
 arch/metag/kernel/cpu/Kconfig               |   53 +++++
 arch/metag/kernel/cpu/Makefile              |    8 +
 arch/metag/kernel/cpu/core-sysfs.c          |   60 ++++++
 arch/metag/kernel/cpu/counters/Makefile     |    7 +
 arch/metag/kernel/cpu/counters/amacount.c   |  227 +++++++++++++++++++++
 arch/metag/kernel/cpu/counters/cyclecount.c |  135 +++++++++++++
 arch/metag/kernel/cpu/counters/perfcount.c  |  154 ++++++++++++++
 arch/metag/kernel/cpu/l2cache-control.c     |  289 +++++++++++++++++++++++++++
 arch/metag/kernel/cpu/memory-arbiter.c      |  133 ++++++++++++
 arch/metag/kernel/cpu/write-combiner.c      |  215 ++++++++++++++++++++
 12 files changed, 1292 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/core-sysfs.h
 create mode 100644 arch/metag/kernel/cpu/Kconfig
 create mode 100644 arch/metag/kernel/cpu/Makefile
 create mode 100644 arch/metag/kernel/cpu/core-sysfs.c
 create mode 100644 arch/metag/kernel/cpu/counters/Makefile
 create mode 100644 arch/metag/kernel/cpu/counters/amacount.c
 create mode 100644 arch/metag/kernel/cpu/counters/cyclecount.c
 create mode 100644 arch/metag/kernel/cpu/counters/perfcount.c
 create mode 100644 arch/metag/kernel/cpu/l2cache-control.c
 create mode 100644 arch/metag/kernel/cpu/memory-arbiter.c
 create mode 100644 arch/metag/kernel/cpu/write-combiner.c

diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index 74cc946..103b1b2 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -278,6 +278,8 @@ menu "Board support"
 
 endmenu
 
+source "arch/metag/kernel/cpu/Kconfig"
+
 source "kernel/Kconfig.preempt"
 
 source kernel/Kconfig.hz
diff --git a/arch/metag/include/asm/core-sysfs.h b/arch/metag/include/asm/core-sysfs.h
new file mode 100644
index 0000000..b2168b1
--- /dev/null
+++ b/arch/metag/include/asm/core-sysfs.h
@@ -0,0 +1,9 @@
+#ifndef _METAG_ASM_CORE_SYSFS_H
+#define _METAG_ASM_CORE_SYSFS_H
+
+#include <linux/device.h>
+
+extern struct bus_type performance_subsys;
+extern struct bus_type cache_subsys;
+
+#endif /* _METAG_ASM_CORE_SYSFS_H */
diff --git a/arch/metag/kernel/cpu/Kconfig b/arch/metag/kernel/cpu/Kconfig
new file mode 100644
index 0000000..f4224b6
--- /dev/null
+++ b/arch/metag/kernel/cpu/Kconfig
@@ -0,0 +1,53 @@
+config META_CORE_SYSFS
+	bool "Meta core sysfs interface"
+	select SYSFS
+	help
+	  This enables a sysfs driver that gives access to the CPU core
+	  parameters such as cycle counters and configuration registers.
+
+config META_WRITE_COMBINER
+	bool "Meta write combiner sysfs interface"
+	depends on META_CORE_SYSFS
+	help
+	  This enables a sysfs driver that gives access to the CPU core
+	  write combiner hardware.
+
+config META_MEM_ARBITER
+	bool "Meta memory arbiter sysfs interface"
+	depends on META_CORE_SYSFS
+	help
+	  This enables a sysfs driver that gives access to the CPU core
+	  memory arbiter hardware.
+
+config META_CYCLE_COUNTER
+	bool "Meta cycle counters"
+	depends on META_CORE_SYSFS
+	help
+	  This enables a sysfs driver that gives access to the CPU core
+	  cycle counters.
+
+config META_PERFORMANCE_COUNTER
+	bool "Meta performance counters"
+	depends on META_CORE_SYSFS
+	help
+	  This enables a sysfs driver that gives access to the CPU core
+	  performance counters.
+
+config META_AMA_COUNTER
+	bool "Meta Automatic MIPs Allocation counters"
+	depends on META_CORE_SYSFS
+	help
+	  This enables a sysfs driver that gives access to the CPU core
+	  Automatic MIPs Allocation counters.
+
+config META_L2C_CONTROL
+	bool "Level 2 Cache control"
+	depends on META_CORE_SYSFS && META_L2C
+	help
+	  This enables a sysfs driver that provides access to the CPU core level
+	  2 cache configuration (information like line size, ways of
+	  associativity, total size, whether unified, and the hardware revision
+	  number). It also provides controls to enable and disable the L2 cache,
+	  to enable and disable prefetch, and to trigger write backs and
+	  flushes, mostly for debug purposes. They can be found in
+	  /sys/devices/system/cache/l2/.
diff --git a/arch/metag/kernel/cpu/Makefile b/arch/metag/kernel/cpu/Makefile
new file mode 100644
index 0000000..f898af1
--- /dev/null
+++ b/arch/metag/kernel/cpu/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Linux Meta-specific sysfs interfaces.
+#
+
+obj-$(CONFIG_META_CORE_SYSFS)		+= core-sysfs.o counters/
+obj-$(CONFIG_META_WRITE_COMBINER)	+= write-combiner.o
+obj-$(CONFIG_META_MEM_ARBITER)		+= memory-arbiter.o
+obj-$(CONFIG_META_L2C_CONTROL)		+= l2cache-control.o
diff --git a/arch/metag/kernel/cpu/core-sysfs.c b/arch/metag/kernel/cpu/core-sysfs.c
new file mode 100644
index 0000000..ebb5d8b
--- /dev/null
+++ b/arch/metag/kernel/cpu/core-sysfs.c
@@ -0,0 +1,60 @@
+/*
+ * linux/arch/metag/drivers/core-sysfs.c
+ *
+ * Meta core sysfs interface, including cycle counters, perf counters and AMA
+ * configuration registers.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/core-sysfs.h>
+#include <linux/device.h>
+#include <linux/init.h>
+
+struct bus_type performance_subsys = {
+	.name = "performance",
+	.dev_name = "counter",
+};
+
+struct bus_type cache_subsys = {
+	.name = "cache",
+	.dev_name = "cache",
+};
+
+static int __init meta_core_sysfs_init(void)
+{
+	int err, ret = 0;
+
+	err = subsys_system_register(&performance_subsys, NULL);
+	if (err) {
+		performance_subsys.name = NULL;
+		ret = err;
+	}
+
+	err = subsys_system_register(&cache_subsys, NULL);
+	if (err) {
+		cache_subsys.name = NULL;
+		ret = err;
+	}
+
+	return ret;
+}
+arch_initcall(meta_core_sysfs_init);
diff --git a/arch/metag/kernel/cpu/counters/Makefile b/arch/metag/kernel/cpu/counters/Makefile
new file mode 100644
index 0000000..6dc877b
--- /dev/null
+++ b/arch/metag/kernel/cpu/counters/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Meta performance counter specific device drivers.
+#
+
+obj-$(CONFIG_META_CYCLE_COUNTER)	+= cyclecount.o
+obj-$(CONFIG_META_PERFORMANCE_COUNTER)	+= perfcount.o
+obj-$(CONFIG_META_AMA_COUNTER)		+= amacount.o
diff --git a/arch/metag/kernel/cpu/counters/amacount.c b/arch/metag/kernel/cpu/counters/amacount.c
new file mode 100644
index 0000000..86f1859
--- /dev/null
+++ b/arch/metag/kernel/cpu/counters/amacount.c
@@ -0,0 +1,227 @@
+/*
+ * linux/arch/metag/drivers/amacount.c
+ *
+ * Meta core Automatic MIPs Allocation (AMA) sysfs interface
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/core_reg.h>
+#include <asm/core-sysfs.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+/* Control unit registers for AMA */
+static unsigned int cu_ama_regs[] = {
+	TXAMAREG0_REGNUM,
+	TXAMAREG1_REGNUM,
+	TXAMAREG2_REGNUM,
+	TXAMAREG3_REGNUM,
+};
+
+#define CU_AMA_REG(thread, reg)					\
+	(T0UCTREG0 + (TnUCTRX_STRIDE * thread) +		\
+	 (TXUCTREGn_STRIDE * cu_ama_regs[reg]))
+
+/* Memory mapped registers for AMA */
+static unsigned int mmap_ama_regs[] = {
+	T0AMAREG4,
+	T0AMAREG5,
+	T0AMAREG6,
+};
+
+#define MMAP_AMA_REG(thread, reg)				\
+	((TnAMAREGX_STRIDE * thread) + mmap_ama_regs[reg])
+
+enum cu_reg_num {reg0, reg1, reg2, reg3};
+enum mmap_reg_num {reg4, reg5, reg6};
+
+static ssize_t show_cu_amareg(unsigned int thread,
+	enum cu_reg_num reg, char *buf)
+{
+	ssize_t err;
+	u32 val;
+
+	val = readl(CU_AMA_REG(thread, reg));
+	err = sprintf(buf, "%u\n", val);
+
+	return err;
+}
+
+static ssize_t store_cu_amareg(unsigned int thread,
+	enum mmap_reg_num reg, const char *buf, size_t count)
+{
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	writel(val, CU_AMA_REG(thread, reg));
+
+	return count;
+}
+
+#define SYSFS_CUREG_SETUP(REG) \
+static ssize_t show_##REG(struct device *dev, \
+			  struct device_attribute *attr, char *buf) \
+{ \
+	return show_cu_amareg(dev->id, REG, buf); \
+} \
+static ssize_t store_##REG(struct device *dev, \
+			   struct device_attribute *attr, const char *buf, \
+			   size_t count) \
+{ \
+	return store_cu_amareg(dev->id, REG, buf, count); \
+}
+
+static ssize_t show_mmap_amareg(unsigned int thread,
+	enum mmap_reg_num reg, char *buf)
+{
+	ssize_t err;
+	u32 val;
+
+	val = readl(MMAP_AMA_REG(thread, reg));
+	err = sprintf(buf, "%u\n", val);
+
+	return err;
+}
+
+static ssize_t store_mmap_amareg(unsigned int thread,
+				 enum mmap_reg_num reg, const char *buf,
+				 size_t count)
+{
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	writel(val, MMAP_AMA_REG(thread, reg));
+
+	return count;
+}
+
+#define SYSFS_MMAPREG_SETUP(REG) \
+static ssize_t show_##REG(struct device *dev, \
+			  struct device_attribute *attr, char *buf) \
+{ \
+	return show_mmap_amareg(dev->id, REG, buf); \
+} \
+static ssize_t store_##REG(struct device *dev, \
+			   struct device_attribute *attr, \
+			   const char *buf, size_t count) \
+{ \
+	return store_mmap_amareg(dev->id, REG, buf, count); \
+}
+
+SYSFS_CUREG_SETUP(reg0);
+SYSFS_CUREG_SETUP(reg1);
+SYSFS_CUREG_SETUP(reg2);
+SYSFS_CUREG_SETUP(reg3);
+
+SYSFS_MMAPREG_SETUP(reg4);
+SYSFS_MMAPREG_SETUP(reg5);
+SYSFS_MMAPREG_SETUP(reg6);
+
+static struct device_attribute cu_ama_attrs[] = {
+	__ATTR(amareg0, 0644, show_reg0, store_reg0),
+	__ATTR(amareg1, 0644, show_reg1, store_reg1),
+	__ATTR(amareg2, 0644, show_reg2, store_reg2),
+	__ATTR(amareg3, 0644, show_reg3, store_reg3),
+};
+
+static struct device_attribute mmap_ama_attrs[] = {
+	__ATTR(amareg4, 0644, show_reg4, store_reg4),
+	__ATTR(amareg5, 0644, show_reg5, store_reg5),
+	__ATTR(amareg6, 0644, show_reg6, store_reg6),
+};
+
+static struct device device_ama = {
+	.bus = &performance_subsys,
+	.init_name = "ama",
+};
+
+static struct device device_ama_threads[4] = {
+	{
+		.id = 0,
+		.bus = &performance_subsys,
+		.parent = &device_ama,
+		.init_name = "thread0",
+	},
+	{
+		.id = 1,
+		.bus = &performance_subsys,
+		.parent = &device_ama,
+		.init_name = "thread1",
+	},
+	{
+		.id = 2,
+		.bus = &performance_subsys,
+		.parent = &device_ama,
+		.init_name = "thread2",
+	},
+	{
+		.id = 3,
+		.bus = &performance_subsys,
+		.parent = &device_ama,
+		.init_name = "thread3",
+	},
+};
+
+static int __init meta_amacount_init(void)
+{
+	int i, thread, exists, ret;
+
+	if (!performance_subsys.name)
+		return -EINVAL;
+
+	ret = device_register(&device_ama);
+	if (ret)
+		return ret;
+
+	for (thread = 0; thread < 4; thread++) {
+		exists = core_reg_read(TXUCT_ID, TXENABLE_REGNUM, thread);
+		if (!exists)
+			break;
+
+		ret = device_register(&device_ama_threads[thread]);
+		if (ret)
+			return ret;
+
+		for (i = 0; i < ARRAY_SIZE(cu_ama_attrs); i++) {
+			ret = device_create_file(&device_ama_threads[thread],
+						 &cu_ama_attrs[i]);
+			if (ret)
+				return ret;
+		}
+		for (i = 0; i < ARRAY_SIZE(mmap_ama_attrs); i++) {
+			ret = device_create_file(&device_ama_threads[thread],
+						 &mmap_ama_attrs[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+device_initcall(meta_amacount_init);
diff --git a/arch/metag/kernel/cpu/counters/cyclecount.c b/arch/metag/kernel/cpu/counters/cyclecount.c
new file mode 100644
index 0000000..3ac8b28
--- /dev/null
+++ b/arch/metag/kernel/cpu/counters/cyclecount.c
@@ -0,0 +1,135 @@
+/*
+ * linux/arch/metag/drivers/cyclecount.c
+ *
+ * Meta core cycle counter sysfs interface
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Make sure we include these first with the TXUXXRX values available,
+ * or else we cannot get hold of them later on after somebody else has
+ * included them from the arch headers.
+ */
+
+#include <asm/core_reg.h>
+#include <asm/core-sysfs.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define IDLE_COUNTER()	(T0UCTREG0 + TXUCTREGn_STRIDE * TXIDLECYC_REGNUM)
+
+#define CYCLE_COUNTER(thread)					\
+	(T0UCTREG0 + (TnUCTRX_STRIDE * thread) +		\
+	 (TXUCTREGn_STRIDE * TXTACTCYC_REGNUM))
+
+enum thread_id {
+	thread0,
+	thread1,
+	thread2,
+	thread3,
+	idle
+};
+
+static ssize_t show_cycles(enum thread_id thread, char *buf)
+{
+	int err = -EINVAL;
+	unsigned int cycles;
+
+	switch (thread) {
+	case thread0:
+	case thread1:
+	case thread2:
+	case thread3:
+		cycles = readl(CYCLE_COUNTER(thread));
+		writel(0, CYCLE_COUNTER(thread));
+		err = sprintf(buf, "%u\n", cycles);
+		break;
+	case idle:
+		cycles = readl(IDLE_COUNTER());
+		writel(0, IDLE_COUNTER());
+		err = sprintf(buf, "%u\n", cycles);
+	}
+
+	return err;
+}
+
+#define SYSFS_CYCLE_SETUP(NAME) \
+static ssize_t show_##NAME(struct device *dev,  \
+			   struct device_attribute *attr, char *buf) \
+{ \
+	return show_cycles(NAME, buf); \
+}
+
+SYSFS_CYCLE_SETUP(thread0);
+SYSFS_CYCLE_SETUP(thread1);
+SYSFS_CYCLE_SETUP(thread2);
+SYSFS_CYCLE_SETUP(thread3);
+SYSFS_CYCLE_SETUP(idle);
+
+static struct device_attribute thread_attrs[] = {
+	__ATTR(thread0, 0444, show_thread0, NULL),
+	__ATTR(thread1, 0444, show_thread1, NULL),
+	__ATTR(thread2, 0444, show_thread2, NULL),
+	__ATTR(thread3, 0444, show_thread3, NULL),
+};
+
+static DEVICE_ATTR(idle, 0444, show_idle, NULL);
+
+static struct device device_perf_cycles = {
+	.bus = &performance_subsys,
+	.init_name = "cycles",
+};
+
+static int __init meta_cyclecount_init(void)
+{
+	int i, exists, ret;
+
+	if (!performance_subsys.name)
+		return -EINVAL;
+
+	ret = device_register(&device_perf_cycles);
+	if (ret)
+		return ret;
+
+
+	/* We always have an idle counter */
+	ret = device_create_file(&device_perf_cycles, &dev_attr_idle);
+	if (ret)
+		return ret;
+
+	/* Check for up to four threads */
+	for (i = 0; i < ARRAY_SIZE(thread_attrs); i++) {
+		exists = core_reg_read(TXUCT_ID, TXENABLE_REGNUM, i);
+		if (exists) {
+			ret = device_create_file(&device_perf_cycles,
+						 &thread_attrs[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+device_initcall(meta_cyclecount_init);
diff --git a/arch/metag/kernel/cpu/counters/perfcount.c b/arch/metag/kernel/cpu/counters/perfcount.c
new file mode 100644
index 0000000..0c982d1
--- /dev/null
+++ b/arch/metag/kernel/cpu/counters/perfcount.c
@@ -0,0 +1,154 @@
+/*
+ * linux/arch/metag/drivers/perfcount.c
+ *
+ * Meta core performance counter sysfs interface
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/core-sysfs.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define METAC_PERF_VALUES
+#include <asm/tbx/machine.inc>
+
+static int counter_map[] = {
+	PERF_COUNT0,
+	PERF_COUNT1
+};
+
+static ssize_t show_counter(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	u32 perf, val;
+
+	val = readl(counter_map[dev->id]);
+	perf = val & PERF_COUNT_BITS;
+	writel(val & ~PERF_COUNT_BITS, counter_map[dev->id]);
+
+	return sprintf(buf, "%u\n", perf);
+}
+
+static ssize_t show_mask(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	u32 mask;
+
+	mask = readl(counter_map[dev->id]) & PERF_THREAD_BITS;
+	return sprintf(buf, "%u\n", mask);
+}
+
+static ssize_t store_mask(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	unsigned long val;
+	u32 read_val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	read_val = readl(counter_map[dev->id]) & ~PERF_THREAD_BITS;
+	val <<= PERF_THREAD_S;
+	writel(read_val | val, counter_map[dev->id]);
+
+	return count;
+}
+
+static ssize_t show_ctrl(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	u32 ctrl;
+
+	ctrl = readl(counter_map[dev->id]) & PERF_CTRL_BITS;
+	return sprintf(buf, "%u\n", ctrl);
+}
+
+static ssize_t store_ctrl(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	unsigned long val;
+	u32 read_val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	read_val = readl(counter_map[dev->id]) & ~PERF_CTRL_BITS;
+	val <<= PERF_CTRL_S;
+	writel(read_val | val, counter_map[dev->id]);
+
+	return count;
+}
+
+static struct device_attribute perf_attrs[] = {
+	__ATTR(counter,	0644, show_counter, NULL),
+	__ATTR(mask,	0644, show_mask, store_mask),
+	__ATTR(ctrl,	0644, show_ctrl, store_ctrl),
+};
+
+static struct device device_perfcount = {
+	.bus = &performance_subsys,
+	.init_name = "perfcount",
+};
+
+static struct device device_perf_counters[] = {
+	{
+		.id = 0,
+		.parent = &device_perfcount,
+		.bus = &performance_subsys,
+	},
+	{
+		.id = 1,
+		.parent = &device_perfcount,
+		.bus = &performance_subsys,
+	},
+};
+
+static int __init meta_perfcount_init(void)
+{
+	int i, j, ret;
+
+	if (!performance_subsys.name)
+		return -EINVAL;
+
+	ret = device_register(&device_perfcount);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(device_perf_counters); ++i) {
+		ret = device_register(&device_perf_counters[i]);
+		if (ret)
+			return ret;
+
+		for (j = 0; j < ARRAY_SIZE(perf_attrs); ++j) {
+			ret = device_create_file(&device_perf_counters[i],
+						 &perf_attrs[j]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+device_initcall(meta_perfcount_init);
diff --git a/arch/metag/kernel/cpu/l2cache-control.c b/arch/metag/kernel/cpu/l2cache-control.c
new file mode 100644
index 0000000..5d17dbe
--- /dev/null
+++ b/arch/metag/kernel/cpu/l2cache-control.c
@@ -0,0 +1,289 @@
+/*
+ * l2cache-control.c
+ *
+ * Meta Level 2 cache sysfs interface
+ *
+ * Copyright (C) 2011-2012 Imagination Technologies Ltd.
+ * Written by James Hogan <james.hogan@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <asm/core-sysfs.h>
+#include <asm/l2cache.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+static ssize_t show_l2c_enabled(struct device *sysdev,
+				struct device_attribute *attr, char *buf)
+{
+	ssize_t err;
+	int val;
+
+	val = !!meta_l2c_is_enabled();
+	err = sprintf(buf, "%d\n", val);
+	return err;
+}
+
+static ssize_t store_l2c_enabled(struct device *sysdev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	if (val) {
+		pr_info("L2 Cache: Enabling... ");
+		if (meta_l2c_enable())
+			pr_cont("already enabled\n");
+		else
+			pr_cont("done\n");
+	} else {
+		pr_info("L2 Cache: Disabling... ");
+		if (meta_l2c_disable())
+			pr_cont("already disabled\n");
+		else
+			pr_cont("done\n");
+	}
+
+	return count;
+}
+
+static ssize_t show_l2c_prefetch(struct device *sysdev,
+				 struct device_attribute *attr, char *buf)
+{
+	ssize_t err;
+	int val;
+
+	val = !!meta_l2c_pf_is_enabled();
+	err = sprintf(buf, "%d\n", val);
+	return err;
+}
+
+static ssize_t store_l2c_prefetch(struct device *sysdev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	if (val) {
+		pr_info("L2 Cache: Enabling prefetch... ");
+		if (meta_l2c_pf_enable(1))
+			pr_cont("already enabled\n");
+		else
+			pr_cont("done\n");
+	} else {
+		pr_info("L2 Cache: Disabling prefetch... ");
+		if (!meta_l2c_pf_enable(0))
+			pr_cont("already disabled\n");
+		else
+			pr_cont("done\n");
+	}
+
+	return count;
+}
+
+static ssize_t show_l2c_writeback(struct device *sysdev,
+				  struct device_attribute *attr, char *buf)
+{
+	ssize_t err;
+
+	/* when read, we return whether the L2 is a writeback cache */
+	err = sprintf(buf, "%d\n", !!meta_l2c_is_writeback());
+	return err;
+}
+
+static ssize_t store_l2c_writeback(struct device *sysdev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	if (val)
+		meta_l2c_writeback();
+
+	return count;
+}
+
+static ssize_t show_l2c_flush(struct device *sysdev,
+			      struct device_attribute *attr, char *buf)
+{
+	ssize_t err;
+
+	err = sprintf(buf, "%d\n", 0);
+	return err;
+}
+
+static ssize_t store_l2c_flush(struct device *sysdev,
+			       struct device_attribute *attr, const char *buf,
+			       size_t count)
+{
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	if (val)
+		meta_l2c_flush();
+
+	return count;
+}
+
+static ssize_t type_show(struct device *sysdev, struct device_attribute *attr,
+			    char *buf)
+{
+	ssize_t err;
+	const char *type;
+
+	if (meta_l2c_is_unified()) {
+		type = "Unified";
+	} else {
+		/*
+		 * Should be "Instruction" or "Data" really, but we're
+		 * representing the L2 cache as a whole.
+		 */
+		type = "Separate";
+	}
+	err = sprintf(buf, "%s\n", type);
+	return err;
+}
+
+static ssize_t level_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	ssize_t err;
+
+	err = sprintf(buf, "%d\n", 2);
+	return err;
+}
+
+static ssize_t size_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	ssize_t err;
+
+	err = sprintf(buf, "%uK\n", meta_l2c_size() >> 10);
+	return err;
+}
+
+static ssize_t coherency_line_size_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	ssize_t err;
+
+	err = sprintf(buf, "%u\n", meta_l2c_linesize());
+	return err;
+}
+
+static ssize_t number_of_sets_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	ssize_t err;
+	unsigned int sets;
+
+	sets = meta_l2c_size() / (meta_l2c_ways() * meta_l2c_linesize());
+	err = sprintf(buf, "%u\n", sets);
+	return err;
+}
+
+static ssize_t ways_of_associativity_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	ssize_t err;
+
+	err = sprintf(buf, "%u\n", meta_l2c_ways());
+	return err;
+}
+
+static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	ssize_t err;
+
+	err = sprintf(buf, "%u\n", meta_l2c_revision());
+	return err;
+}
+
+static ssize_t config_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	ssize_t err;
+
+	err = sprintf(buf, "0x%08x\n", meta_l2c_config());
+	return err;
+}
+
+static struct device_attribute l2c_attrs[] = {
+	/*
+	 * These are fairly standard attributes, used by other architectures in
+	 * /sys/devices/system/cpu/cpuX/cache/indexX/ (but on Meta they're
+	 * elsewhere).
+	 */
+	__ATTR_RO(type),
+	__ATTR_RO(level),
+	__ATTR_RO(size),
+	__ATTR_RO(coherency_line_size),
+	__ATTR_RO(number_of_sets),
+	__ATTR_RO(ways_of_associativity),
+
+	/*
+	 * Other read only attributes, specific to Meta.
+	 */
+	__ATTR_RO(revision),
+	__ATTR_RO(config),
+
+	/*
+	 * These can be used to perform operations on the cache, such as
+	 * enabling the cache and prefetch, and triggering a full writeback or
+	 * flush.
+	 */
+	__ATTR(enabled,   0644, show_l2c_enabled, store_l2c_enabled),
+	__ATTR(prefetch,  0644, show_l2c_prefetch, store_l2c_prefetch),
+	__ATTR(writeback, 0644, show_l2c_writeback, store_l2c_writeback),
+	__ATTR(flush,     0644, show_l2c_flush, store_l2c_flush),
+};
+
+static struct device device_cache_l2 = {
+	.bus = &cache_subsys,
+	.init_name = "l2",
+};
+
+static int __init meta_l2c_sysfs_init(void)
+{
+	int i, ret;
+
+	if (!cache_subsys.name)
+		return -EINVAL;
+
+	/* if there's no L2 cache, don't add the sysfs nodes */
+	if (!meta_l2c_is_present())
+		return 0;
+
+	ret = device_register(&device_cache_l2);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(l2c_attrs); i++) {
+		ret = device_create_file(&device_cache_l2,
+					 &l2c_attrs[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+device_initcall(meta_l2c_sysfs_init);
diff --git a/arch/metag/kernel/cpu/memory-arbiter.c b/arch/metag/kernel/cpu/memory-arbiter.c
new file mode 100644
index 0000000..85bda71
--- /dev/null
+++ b/arch/metag/kernel/cpu/memory-arbiter.c
@@ -0,0 +1,133 @@
+/*
+ * linux/arch/metag/drivers/write_combiner.c
+ *
+ * Meta memory arbiter sysfs interface
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Make sure we include these first with the TXUXXRX values available,
+ * or else we cannot get hold of them later on after somebody else has
+ * included them from the arch headers.
+ */
+
+#include <asm/core_reg.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define MEMARBITER_REG(thread)						\
+	(EXPAND_T0ARBITER + (EXPAND_TnARBITER_STRIDE * thread))
+
+enum thread_id {
+	thread0,
+	thread1,
+	thread2,
+	thread3
+};
+
+static ssize_t show_memarbiter(enum thread_id thread, char *buf)
+{
+	ssize_t err;
+	u32 val;
+
+	val = readl(MEMARBITER_REG(thread));
+	err = sprintf(buf, "%u\n", val);
+	return err;
+}
+
+static void store_memarbiter(enum thread_id thread, const char *buf)
+{
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return;
+
+	writel(val, MEMARBITER_REG(thread));
+}
+
+#define SYSFS_MEMARBITER_SETUP(NAME) \
+static ssize_t show_##NAME##_ma(struct device *dev,  \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return show_memarbiter(NAME, buf); \
+} \
+static ssize_t store_##NAME##_ma(struct device *dev, \
+				struct device_attribute *attr, \
+				const char *buf, size_t count) \
+{ \
+	store_memarbiter(NAME, buf); \
+	return count; \
+}
+
+SYSFS_MEMARBITER_SETUP(thread0);
+SYSFS_MEMARBITER_SETUP(thread1);
+SYSFS_MEMARBITER_SETUP(thread2);
+SYSFS_MEMARBITER_SETUP(thread3);
+
+static DEVICE_ATTR(thread0, 0644, show_thread0_ma, store_thread0_ma);
+static DEVICE_ATTR(thread1, 0644, show_thread1_ma, store_thread1_ma);
+static DEVICE_ATTR(thread2, 0644, show_thread2_ma, store_thread2_ma);
+static DEVICE_ATTR(thread3, 0644, show_thread3_ma, store_thread3_ma);
+
+static struct attribute *memory_arbiter_root_attrs[] = {
+	&dev_attr_thread0.attr,
+	&dev_attr_thread1.attr,
+	&dev_attr_thread2.attr,
+	&dev_attr_thread3.attr,
+	NULL,
+};
+
+static struct attribute_group memory_arbiter_root_attr_group = {
+	.attrs = memory_arbiter_root_attrs,
+};
+
+static const struct attribute_group *memory_arbiter_root_attr_groups[] = {
+	&memory_arbiter_root_attr_group,
+	NULL,
+};
+
+struct bus_type memory_arbiter_subsys = {
+	.name = "memory_arbiter",
+	.dev_name = "ma",
+};
+
+static int __init meta_memarbiter_init(void)
+{
+	int i, exists, ret;
+
+	/* modify number of threads displayed */
+	for (i = 0; i < ARRAY_SIZE(memory_arbiter_root_attrs); i++) {
+		exists = core_reg_read(TXUCT_ID, TXENABLE_REGNUM, i);
+		if (!exists) {
+			memory_arbiter_root_attrs[i] = NULL;
+			break;
+		}
+	}
+
+	ret = subsys_system_register(&memory_arbiter_subsys,
+				     memory_arbiter_root_attr_groups);
+	return ret;
+}
+device_initcall(meta_memarbiter_init);
diff --git a/arch/metag/kernel/cpu/write-combiner.c b/arch/metag/kernel/cpu/write-combiner.c
new file mode 100644
index 0000000..fe9fb35
--- /dev/null
+++ b/arch/metag/kernel/cpu/write-combiner.c
@@ -0,0 +1,215 @@
+/*
+ * linux/arch/metag/drivers/write_combiner.c
+ *
+ * Meta write combiner sysfs interface
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Make sure we include these first with the TXUXXRX values available,
+ * or else we cannot get hold of them later on after somebody else has
+ * included them from the arch headers.
+ */
+
+#include <asm/core_reg.h>
+#include <asm/core-sysfs.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define WRCOMBINER_REG(thread)						\
+	(EXPAND_T0WRCOMBINE + (EXPAND_TnWRCOMBINE_STRIDE * thread))
+
+enum thread_id {
+	thread0,
+	thread1,
+	thread2,
+	thread3
+};
+
+static ssize_t show_wrcombiner(enum thread_id thread, char *buf)
+{
+	ssize_t err;
+	u32 val;
+
+	val = readl(WRCOMBINER_REG(thread));
+	err = sprintf(buf, "%u\n", val);
+	return err;
+}
+
+static ssize_t store_wrcombiner(enum thread_id thread, const char *buf,
+				size_t count)
+{
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	writel(val, WRCOMBINER_REG(thread));
+
+	return count;
+}
+
+#define SYSFS_WRCOMBINER_SETUP(NAME) \
+static ssize_t show_##NAME##_wc(struct device *dev,  \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return show_wrcombiner(NAME, buf); \
+} \
+static ssize_t store_##NAME##_wc(struct device *dev, \
+				 struct device_attribute *attr, \
+				 const char *buf, size_t count) \
+{ \
+	return store_wrcombiner(NAME, buf, count); \
+}
+
+static ssize_t show_perfchan0(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	ssize_t err;
+	u32 val;
+
+	val = readl(EXPAND_PERFCHAN0);
+	err = sprintf(buf, "%u\n", val);
+	return err;
+}
+
+static ssize_t store_perfchan0(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	u32 read_val;
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	read_val = readl(EXPAND_PERFCHAN0) & ~EXPPERF_CTRL_BITS;
+	writel(read_val | val, EXPAND_PERFCHAN0);
+	return count;
+}
+
+static ssize_t show_perfchan1(struct device *sysdev,
+			      struct device_attribute *attr, char *buf)
+{
+	ssize_t err;
+	u32 val;
+
+	val = readl(EXPAND_PERFCHAN1);
+	err = sprintf(buf, "%u\n", val);
+	return err;
+}
+
+static ssize_t store_perfchan1(struct device *sysdev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	u32 read_val;
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	read_val = readl(EXPAND_PERFCHAN1) & ~EXPPERF_CTRL_BITS;
+	writel(read_val | val, EXPAND_PERFCHAN1);
+	return count;
+}
+
+SYSFS_WRCOMBINER_SETUP(thread0);
+SYSFS_WRCOMBINER_SETUP(thread1);
+SYSFS_WRCOMBINER_SETUP(thread2);
+SYSFS_WRCOMBINER_SETUP(thread3);
+
+static DEVICE_ATTR(thread0, 0644, show_thread0_wc, store_thread0_wc);
+static DEVICE_ATTR(thread1, 0644, show_thread1_wc, store_thread1_wc);
+static DEVICE_ATTR(thread2, 0644, show_thread2_wc, store_thread2_wc);
+static DEVICE_ATTR(thread3, 0644, show_thread3_wc, store_thread3_wc);
+
+static struct attribute *write_combiner_root_attrs[] = {
+	&dev_attr_thread0.attr,
+	&dev_attr_thread1.attr,
+	&dev_attr_thread2.attr,
+	&dev_attr_thread3.attr,
+	NULL,
+};
+
+static struct attribute_group write_combiner_root_attr_group = {
+	.attrs = write_combiner_root_attrs,
+};
+
+static const struct attribute_group *write_combiner_root_attr_groups[] = {
+	&write_combiner_root_attr_group,
+	NULL,
+};
+
+static struct device_attribute perfchan_attrs[] = {
+	__ATTR(perfchan0, 0644, show_perfchan0, store_perfchan0),
+	__ATTR(perfchan1, 0644, show_perfchan1, store_perfchan1),
+};
+
+struct bus_type write_combiner_subsys = {
+	.name = "write_combiner",
+	.dev_name = "wc",
+};
+
+static struct device device_perf_write_combiner = {
+	.bus = &performance_subsys,
+	.init_name = "write_combiner",
+};
+
+static int __init meta_writecombiner_init(void)
+{
+	int i, exists, ret;
+
+	/* modify number of threads displayed */
+	for (i = 0; i < ARRAY_SIZE(write_combiner_root_attrs); i++) {
+		exists = core_reg_read(TXUCT_ID, TXENABLE_REGNUM, i);
+		if (!exists) {
+			write_combiner_root_attrs[i] = NULL;
+			break;
+		}
+	}
+
+	ret = subsys_system_register(&write_combiner_subsys,
+				     write_combiner_root_attr_groups);
+	if (ret)
+		return ret;
+
+	if (!performance_subsys.name)
+		return -EINVAL;
+
+	ret = device_register(&device_perf_write_combiner);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(perfchan_attrs); i++) {
+		ret = device_create_file(&device_perf_write_combiner,
+					 &perfchan_attrs[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+device_initcall(meta_writecombiner_init);
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 38/40] metag: add JTAG Debug Adapter (DA) support
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (34 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 37/40] metag: Various sysfs drivers James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14 ` [RFC PATCH v1 39/40] tty/metag_da: add metag DA TTY driver James Hogan
                   ` (4 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan

Add basic JTAG Debug Adapter (DA) support so that drivers which
communicate with the DA can detect whether one is actually present
(otherwise the target will halt indefinitely).

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 arch/metag/Kconfig                     |    9 ++++++
 arch/metag/configs/meta1_defconfig     |    1 +
 arch/metag/configs/meta2_defconfig     |    1 +
 arch/metag/configs/meta2_smp_defconfig |    1 +
 arch/metag/include/asm/da.h            |   43 ++++++++++++++++++++++++++++++++
 arch/metag/kernel/Makefile             |    1 +
 arch/metag/kernel/da.c                 |   24 +++++++++++++++++
 arch/metag/kernel/setup.c              |    3 ++
 8 files changed, 83 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/include/asm/da.h
 create mode 100644 arch/metag/kernel/da.c

diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index 103b1b2..acf04b6 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -214,6 +214,15 @@ config META_PERFCOUNTER_IRQS
 	  When disabled, Performance Counters information will be collected
 	  based on Timer Interrupt.
 
+config METAG_DA
+	bool "DA support"
+	help
+	  Say Y if you plan to use a DA debug adapter with Linux. The presence
+	  of the DA will be detected automatically at boot, so it is safe to say
+	  Y to this option even when booting without a DA.
+
+	  This enables support for services provided by DA JTAG debug adapters.
+
 menu "Boot options"
 
 config METAG_BUILTIN_DTB
diff --git a/arch/metag/configs/meta1_defconfig b/arch/metag/configs/meta1_defconfig
index eb01dde..e943a95 100644
--- a/arch/metag/configs/meta1_defconfig
+++ b/arch/metag/configs/meta1_defconfig
@@ -17,6 +17,7 @@ CONFIG_PARTITION_ADVANCED=y
 # CONFIG_IOSCHED_CFQ is not set
 CONFIG_FLATMEM_MANUAL=y
 CONFIG_META12_FPGA=y
+CONFIG_METAG_DA=y
 CONFIG_HZ_100=y
 CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
diff --git a/arch/metag/configs/meta2_defconfig b/arch/metag/configs/meta2_defconfig
index 1d28258..2d246f6 100644
--- a/arch/metag/configs/meta2_defconfig
+++ b/arch/metag/configs/meta2_defconfig
@@ -18,6 +18,7 @@ CONFIG_PARTITION_ADVANCED=y
 CONFIG_META_L2C=y
 CONFIG_FLATMEM_MANUAL=y
 CONFIG_META_HALT_ON_PANIC=y
+CONFIG_METAG_DA=y
 CONFIG_HZ_100=y
 CONFIG_DEVTMPFS=y
 # CONFIG_STANDALONE is not set
diff --git a/arch/metag/configs/meta2_smp_defconfig b/arch/metag/configs/meta2_smp_defconfig
index 979107b..a2120ec 100644
--- a/arch/metag/configs/meta2_smp_defconfig
+++ b/arch/metag/configs/meta2_smp_defconfig
@@ -19,6 +19,7 @@ CONFIG_META_L2C=y
 CONFIG_FLATMEM_MANUAL=y
 CONFIG_META_HALT_ON_PANIC=y
 CONFIG_SMP=y
+CONFIG_METAG_DA=y
 CONFIG_HZ_100=y
 CONFIG_DEVTMPFS=y
 # CONFIG_STANDALONE is not set
diff --git a/arch/metag/include/asm/da.h b/arch/metag/include/asm/da.h
new file mode 100644
index 0000000..81bd521
--- /dev/null
+++ b/arch/metag/include/asm/da.h
@@ -0,0 +1,43 @@
+/*
+ * Meta DA JTAG debugger control.
+ *
+ * Copyright 2012 Imagination Technologies Ltd.
+ */
+
+#ifndef _METAG_DA_H_
+#define _METAG_DA_H_
+
+#ifdef CONFIG_METAG_DA
+
+#include <linux/init.h>
+#include <linux/types.h>
+
+extern bool _metag_da_present;
+
+/**
+ * metag_da_enabled() - Find whether a DA is currently enabled.
+ *
+ * Returns:	true if a DA was detected, false if not.
+ */
+static inline bool metag_da_enabled(void)
+{
+	return _metag_da_present;
+}
+
+/**
+ * metag_da_probe() - Try and detect a connected DA.
+ *
+ * This is used at start up to detect whether a DA is active.
+ *
+ * Returns:	0 on detection, -err otherwise.
+ */
+int __init metag_da_probe(void);
+
+#else /* !CONFIG_METAG_DA */
+
+#define metag_da_enabled() false
+#define metag_da_probe() do {} while (0)
+
+#endif
+
+#endif /* _METAG_DA_H_ */
diff --git a/arch/metag/kernel/Makefile b/arch/metag/kernel/Makefile
index 5482817..8d74cfa 100644
--- a/arch/metag/kernel/Makefile
+++ b/arch/metag/kernel/Makefile
@@ -28,6 +28,7 @@ obj-y	+= traps.o
 obj-y	+= user_gateway.o
 
 obj-$(CONFIG_METAG_COREMEM)		+= coremem.o
+obj-$(CONFIG_METAG_DA)			+= da.o
 obj-$(CONFIG_DYNAMIC_FTRACE)		+= ftrace.o
 obj-$(CONFIG_FUNCTION_TRACER)		+= ftrace_stub.o
 obj-$(CONFIG_META_PERFCOUNTER_IRQS)	+= irq_internal.o
diff --git a/arch/metag/kernel/da.c b/arch/metag/kernel/da.c
new file mode 100644
index 0000000..616f02c
--- /dev/null
+++ b/arch/metag/kernel/da.c
@@ -0,0 +1,24 @@
+/*
+ * Meta DA JTAG debugger control.
+ *
+ * Copyright 2012 Imagination Technologies Ltd.
+ */
+
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <asm/da.h>
+#define METAG_TRIG_VALUES
+#include <asm/tbx/machine.inc>
+
+bool _metag_da_present;
+
+int __init metag_da_probe(void)
+{
+	_metag_da_present = (readl(T0VECINT_BHALT) == 1);
+	if (_metag_da_present)
+		pr_info("DA present\n");
+	else
+		pr_info("DA not present\n");
+	return 0;
+}
diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c
index 41d6ad9..83e45f3 100644
--- a/arch/metag/kernel/setup.c
+++ b/arch/metag/kernel/setup.c
@@ -37,6 +37,7 @@
 #include <asm/hwthread.h>
 #include <asm/mmzone.h>
 #include <asm/l2cache.h>
+#include <asm/da.h>
 #include <asm/prom.h>
 #include <asm/mach/arch.h>
 #include <asm/core_reg.h>
@@ -182,6 +183,8 @@ void __init setup_arch(char **cmdline_p)
 	PTBISEG p_heap;
 	int heap_id, i;
 
+	metag_da_probe();
+
 	/* try interpreting the argument as a device tree */
 	machine_desc = setup_machine_fdt(original_cmd_line);
 	/* if it doesn't look like a device tree it must be a command line */
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 39/40] tty/metag_da: add metag DA TTY driver
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (35 preceding siblings ...)
  2012-10-31 16:14 ` [RFC PATCH v1 38/40] metag: add JTAG Debug Adapter (DA) support James Hogan
@ 2012-10-31 16:14 ` James Hogan
  2012-10-31 16:14   ` James Hogan
                   ` (3 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan, Greg Kroah-Hartman

Add a TTY driver for communicating over a Meta DA (Debug Adapter)
channel using the bios channel SWITCH operation.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 MAINTAINERS                            |    1 +
 arch/metag/Kconfig                     |    4 +-
 arch/metag/configs/meta1_defconfig     |    2 +
 arch/metag/configs/meta2_defconfig     |    2 +
 arch/metag/configs/meta2_smp_defconfig |    2 +
 arch/metag/kernel/setup.c              |   12 +
 drivers/tty/Kconfig                    |   13 +
 drivers/tty/Makefile                   |    1 +
 drivers/tty/metag_da.c                 |  503 ++++++++++++++++++++++++++++++++
 9 files changed, 539 insertions(+), 1 deletions(-)
 create mode 100644 drivers/tty/metag_da.c

diff --git a/MAINTAINERS b/MAINTAINERS
index e1d8299..1e55a5e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4537,6 +4537,7 @@ S:	Maintained
 F:	arch/metag/
 F:	Documentation/metag/
 F:	Documentation/devicetree/bindings/metag/
+F:	drivers/tty/metag_da.c
 
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index acf04b6..96e9fbf 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -221,7 +221,9 @@ config METAG_DA
 	  of the DA will be detected automatically at boot, so it is safe to say
 	  Y to this option even when booting without a DA.
 
-	  This enables support for services provided by DA JTAG debug adapters.
+	  This enables support for services provided by DA JTAG debug adapters,
+	  such as:
+	  - communication over DA channels (such as the console driver).
 
 menu "Boot options"
 
diff --git a/arch/metag/configs/meta1_defconfig b/arch/metag/configs/meta1_defconfig
index e943a95..74504f5 100644
--- a/arch/metag/configs/meta1_defconfig
+++ b/arch/metag/configs/meta1_defconfig
@@ -31,6 +31,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
 # CONFIG_SERIO is not set
 # CONFIG_VT is not set
 # CONFIG_LEGACY_PTYS is not set
+CONFIG_DA_TTY=y
+CONFIG_DA_CONSOLE=y
 # CONFIG_DEVKMEM is not set
 # CONFIG_HW_RANDOM is not set
 # CONFIG_HWMON is not set
diff --git a/arch/metag/configs/meta2_defconfig b/arch/metag/configs/meta2_defconfig
index 2d246f6..6f36973 100644
--- a/arch/metag/configs/meta2_defconfig
+++ b/arch/metag/configs/meta2_defconfig
@@ -31,6 +31,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
 # CONFIG_SERIO is not set
 # CONFIG_VT is not set
 # CONFIG_LEGACY_PTYS is not set
+CONFIG_DA_TTY=y
+CONFIG_DA_CONSOLE=y
 # CONFIG_DEVKMEM is not set
 # CONFIG_HW_RANDOM is not set
 # CONFIG_HWMON is not set
diff --git a/arch/metag/configs/meta2_smp_defconfig b/arch/metag/configs/meta2_smp_defconfig
index a2120ec..69de541 100644
--- a/arch/metag/configs/meta2_smp_defconfig
+++ b/arch/metag/configs/meta2_smp_defconfig
@@ -32,6 +32,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
 # CONFIG_SERIO is not set
 # CONFIG_VT is not set
 # CONFIG_LEGACY_PTYS is not set
+CONFIG_DA_TTY=y
+CONFIG_DA_CONSOLE=y
 # CONFIG_DEVKMEM is not set
 # CONFIG_HW_RANDOM is not set
 # CONFIG_HWMON is not set
diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c
index 83e45f3..2d001d4 100644
--- a/arch/metag/kernel/setup.c
+++ b/arch/metag/kernel/setup.c
@@ -61,6 +61,11 @@ extern char _heap_start[];
 extern u32 __dtb_start[];
 #endif
 
+#ifdef CONFIG_DA_CONSOLE
+/* Our early channel based console driver */
+extern struct console dash_console;
+#endif
+
 struct machine_desc *machine_desc __initdata;
 
 /*
@@ -184,6 +189,13 @@ void __init setup_arch(char **cmdline_p)
 	int heap_id, i;
 
 	metag_da_probe();
+#ifdef CONFIG_DA_CONSOLE
+	if (metag_da_enabled()) {
+		/* An early channel based console driver */
+		register_console(&dash_console);
+		add_preferred_console("ttyDA", 1, NULL);
+	}
+#endif
 
 	/* try interpreting the argument as a device tree */
 	machine_desc = setup_machine_fdt(original_cmd_line);
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 830cd62..d38f34d 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -388,3 +388,16 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE
 	  If the number you specify is not a valid byte channel handle, then
 	  there simply will be no early console output.  This is true also
 	  if you don't boot under a hypervisor at all.
+
+config DA_TTY
+	bool "DA TTY"
+	depends on METAG_DA
+	select SERIAL_NONSTANDARD
+	help
+	  This enables a TTY on a Dash channel.
+
+config DA_CONSOLE
+	bool "DA Console"
+	depends on DA_TTY
+	help
+	  This enables a console on a Dash channel.
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 2953059..dbd7745 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -27,5 +27,6 @@ obj-$(CONFIG_SYNCLINK_GT)	+= synclink_gt.o
 obj-$(CONFIG_SYNCLINKMP)	+= synclinkmp.o
 obj-$(CONFIG_SYNCLINK)		+= synclink.o
 obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
+obj-$(CONFIG_DA_TTY)	+= metag_da.o
 
 obj-y += ipwireless/
diff --git a/drivers/tty/metag_da.c b/drivers/tty/metag_da.c
new file mode 100644
index 0000000..c3f1680
--- /dev/null
+++ b/drivers/tty/metag_da.c
@@ -0,0 +1,503 @@
+/*
+ *  dashtty.c - tty driver for Dash channels interface.
+ *
+ *  Copyright (C) 2007,2008,2012 Imagination Technologies
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/console.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+
+#include <asm/da.h>
+
+/* Channel error codes */
+#define CONAOK	0
+#define CONERR	1
+#define CONBAD	2
+#define CONPRM	3
+#define CONADR	4
+#define CONCNT	5
+#define CONCBF	6
+#define CONCBE	7
+#define CONBSY	8
+
+/* Default channel for the console */
+#define CONSOLE_CHANNEL      1
+
+/* First channel available for tty use. */
+#define FIRST_TTY_CHANNEL    0
+
+#define NUM_TTY_CHANNELS     6
+
+/* Auto allocate */
+#define DA_TTY_MAJOR        0
+
+/* A speedy poll rate helps the userland debug process connection response.
+ * But, if you set it too high then no other userland processes get much
+ * of a look in.
+ */
+#define DA_TTY_POLL (HZ / 50)
+
+static int num_channels_need_poll;
+
+static struct timer_list poll_timer;
+
+static struct tty_driver *channel_driver;
+
+static struct task_struct *dashtty_thread;
+
+#define RX_BUF_SIZE 1024
+
+enum {
+	INCHR = 1,
+	OUTCHR,
+	RDBUF,
+	WRBUF,
+	RDSTAT
+};
+
+/* One struct dashtty exists per open channel. */
+struct dashtty {
+	struct tty_struct *tty;
+	int count;
+	char rx_buf[RX_BUF_SIZE];
+};
+
+struct dashbuf {
+	struct list_head entry;
+	struct dashtty *tty;
+	unsigned char *buf;
+	int count;
+	int chan;
+};
+
+static struct dashtty *dashtty_ttys[NUM_TTY_CHANNELS];
+static struct list_head dashtty_output_queue;
+static spinlock_t dashtty_q_lock;
+static wait_queue_head_t dashtty_waitqueue;
+
+/*
+ * Low-level DA channel access routines
+ */
+static int chancall(int bios_function, int channel, int arg2, void *arg3,
+			void *arg4)
+{
+	register int   bios_function__ __asm__("D1Ar1") = bios_function;
+	register int   channel__       __asm__("D0Ar2") = channel;
+	register int   arg2__          __asm__("D1Ar3") = arg2;
+	register void *arg3__          __asm__("D0Ar4") = arg3;
+	register void *arg4__          __asm__("D1Ar5") = arg4;
+	register int   bios_call       __asm__("D0Ar6") = 3;
+	register int   result          __asm__("D0Re0");
+
+	__asm__ volatile (
+		"SETL   [A0StP++], %6,%5\n\t"
+		"SETL   [A0StP++], %4,%3\n\t"
+		"SETL   [A0StP++], %2,%1\n\t"
+		"ADD    A0StP, A0StP, #8\n\t"
+		"SWITCH #0x0C30208\n\t"
+		"GETD   %0, [A0StP+#-8]\n\t"
+		"SUB    A0StP, A0StP, #(4*6)+8\n\t"
+		: "=r" (result)/* outs */
+		: "r" (bios_function__), "r" (channel__), "r" (arg2__),
+		  "r" (arg3__), "r" (arg4__), "r" (bios_call) /* ins */
+		: "memory");
+
+	return result;
+}
+
+/*
+ * Attempts to fetch count bytes from channel channel and returns actual count.
+ */
+static int fetch_data(int channel)
+{
+	struct dashtty *dashtty = dashtty_ttys[channel];
+	struct tty_struct *tty = dashtty->tty;
+	int received = 0;
+
+	if (chancall(RDBUF, channel, RX_BUF_SIZE, (void *)dashtty->rx_buf,
+		     &received) == CONAOK) {
+		if (received) {
+			int space;
+			unsigned char *cbuf;
+
+			space = tty_prepare_flip_string(tty, &cbuf, received);
+
+			if (space <= 0)
+				return 0;
+
+			memcpy(cbuf, dashtty->rx_buf, space);
+			tty_flip_buffer_push(tty);
+		}
+	}
+
+	return received;
+}
+
+/*
+ *	Finds the next channel to poll
+ */
+static int find_channel_to_poll(void)
+{
+	static int last_polled_channel = FIRST_TTY_CHANNEL;
+
+	int this_channel = last_polled_channel;
+
+	while (++this_channel != last_polled_channel) {
+		if (this_channel >= NUM_TTY_CHANNELS)
+			this_channel = 0;
+
+		if (dashtty_ttys[this_channel]) {
+			last_polled_channel = this_channel;
+			break;
+		}
+	}
+	return last_polled_channel;
+}
+
+static int put_data(void *arg)
+{
+	struct dashbuf *dbuf;
+	int number_written;
+
+	__set_current_state(TASK_RUNNING);
+	while (!kthread_should_stop()) {
+		/*
+		 * Pick up all the output buffers and write them out.
+		 *
+		 * FIXME: should we check with ASE how much room we have?
+		 * Ideally, this will already have been done by write_room ??
+		 */
+		spin_lock(&dashtty_q_lock);
+
+		while (!list_empty(&dashtty_output_queue)) {
+			dbuf = list_entry(dashtty_output_queue.next,
+					  struct dashbuf, entry);
+
+			chancall(WRBUF, dbuf->chan, dbuf->count,
+				 (void *)dbuf->buf, &number_written);
+
+			if (list_is_last(&dbuf->entry,
+					 &dashtty_output_queue))
+				list_del_init(&dashtty_output_queue);
+			else
+				list_del(&dbuf->entry);
+
+			/*
+			 * FIXME: should we only be doing wake when we
+			 * know something is sleeping?
+			 */
+			tty_wakeup(dbuf->tty->tty);
+
+			kfree(dbuf->buf);
+			kfree(dbuf);
+		}
+		spin_unlock(&dashtty_q_lock);
+
+		wait_event_interruptible(dashtty_waitqueue,
+					 !list_empty(&dashtty_output_queue));
+	}
+
+	return 0;
+}
+
+/*
+ *	This gets called every DA_TTY_POLL and polls the channels for data
+ */
+static void dashtty_timer(unsigned long ignored)
+{
+	struct dashtty *dtty;
+	int this_channel;
+
+	/* If there are no ports open do nothing and don't poll again. */
+	if (num_channels_need_poll == 0)
+		return;
+
+	this_channel = find_channel_to_poll();
+	dtty = dashtty_ttys[this_channel];
+
+	/* Did we find a channel to poll ? */
+	if (dtty)
+		fetch_data(this_channel);
+
+	mod_timer_pinned(&poll_timer, jiffies + DA_TTY_POLL);
+}
+
+static void add_poll_timer(struct timer_list *poll_timer)
+{
+	init_timer(poll_timer);
+
+	poll_timer->function = dashtty_timer;
+	poll_timer->expires = jiffies + DA_TTY_POLL;
+
+	/*
+	 * Always attach the timer to the boot CPU.
+	 */
+	add_timer_on(poll_timer, 0);
+}
+
+static int dashtty_open(struct tty_struct *tty, struct file *filp)
+{
+	struct dashtty *dashtty;
+	int line;
+
+	if (!tty)
+		return -ENODEV;
+
+	line = tty->index;
+
+	if (line < 0 || line >= NUM_TTY_CHANNELS)
+		return -ENODEV;
+
+	dashtty = dashtty_ttys[line];
+	if (!dashtty) {
+		dashtty = kzalloc(sizeof(struct dashtty), GFP_KERNEL);
+
+		if (!dashtty)
+			return -ENOMEM;
+
+		dashtty->tty = tty;
+		dashtty_ttys[line] = dashtty;
+	}
+
+	dashtty_ttys[line]->count++;
+
+	/*
+	 * Don't add the poll timer if we're opening a console. This
+	 * avoids the overhead of polling the Dash but means it is not
+	 * possible to have a login on /dev/console.
+	 *
+	 */
+	if (line == CONSOLE_CHANNEL)
+		return 0;
+
+	if (num_channels_need_poll == 0)
+		add_poll_timer(&poll_timer);
+
+	num_channels_need_poll++;
+
+	return 0;
+}
+
+static void dashtty_close(struct tty_struct *tty, struct file *filp)
+{
+	int line;
+
+	if (!tty)
+		return;
+
+	line = tty->index;
+
+	if (line < 0 || line >= NUM_TTY_CHANNELS)
+		return;
+
+	dashtty_ttys[line]->count--;
+
+	if (!dashtty_ttys[line]->count) {
+		kfree(dashtty_ttys[line]);
+		dashtty_ttys[line] = NULL;
+	}
+
+	if (line == CONSOLE_CHANNEL)
+		return;
+
+	num_channels_need_poll--;
+
+	if (num_channels_need_poll <= 0)
+		del_timer(&poll_timer);
+}
+
+static int dashtty_write(struct tty_struct *tty, const unsigned char *buf,
+			 int count)
+{
+	struct dashtty *dtty;
+	struct dashbuf *dbuf;
+	int channel;
+
+	if (count <= 0)
+		return 0;
+
+	/* Determine the channel */
+	channel = FIRST_TTY_CHANNEL + tty->index;
+	dtty = dashtty_ttys[channel];
+	BUG_ON(!dtty);
+
+	dbuf = kzalloc(sizeof(*dbuf), GFP_KERNEL);
+	if (!dbuf)
+		return 0;
+
+	dbuf->buf = kzalloc(count, GFP_KERNEL);
+	if (!dbuf->buf) {
+		kfree(dbuf);
+		return 0;
+	}
+
+	memcpy(dbuf->buf, buf, count);
+	dbuf->count = count;
+	dbuf->chan = channel;
+	dbuf->tty = dtty;
+
+	/*
+	 * Enqueue the output buffer.
+	 *
+	 * The reason that we asynchronously write the buffer is because
+	 * there's no way to tell what CPU we're currently executing on.
+	 * If we were to write the buffer synchornously then because dash
+	 * channels are per-CPU the buffer would be written to the channel
+	 * of whatever CPU we're running on.
+	 *
+	 * What we actually want to happen is have all input and output
+	 * done on one CPU.
+	 */
+	spin_lock(&dashtty_q_lock);
+	list_add_tail(&dbuf->entry, &dashtty_output_queue);
+	spin_unlock(&dashtty_q_lock);
+
+	wake_up_interruptible(&dashtty_waitqueue);
+
+	/*
+	 * FIXME: This is slightly optimistic. Because we're deferring
+	 * the output until later it is impossible to predict whether we
+	 * will actually write "count" bytes.
+	 */
+	return count;
+}
+
+/*
+ * Ask the stub how much room there is in the channel
+ */
+static int dashtty_write_room(struct tty_struct *tty)
+{
+	unsigned int data[2];
+	static unsigned int actual_space;
+	int channel;
+
+	if (actual_space)
+		return actual_space;
+
+	channel = FIRST_TTY_CHANNEL + tty->index;
+
+	/* Is there any data waiting ? */
+	if (chancall(RDSTAT, channel, 0, NULL, &data[0]) == CONAOK) {
+		/* There was a bug in LogieDash (1.2.8a at least) where
+		 * the two values were returned the WRONG WAY AROUND!!!
+		 */
+		actual_space = data[1];
+	}
+
+	return actual_space;
+}
+
+static int dashtty_chars_in_buffer(struct tty_struct *tty)
+{
+	return 0;               /* we're not buffering */
+}
+
+static const struct tty_operations dashtty_ops = {
+	.open = dashtty_open,
+	.close = dashtty_close,
+	.write = dashtty_write,
+	.write_room = dashtty_write_room,
+	.chars_in_buffer = dashtty_chars_in_buffer,
+};
+
+static int __init dashtty_init(void)
+{
+	int ret;
+
+	if (!metag_da_enabled())
+		return -ENODEV;
+
+	channel_driver = alloc_tty_driver(NUM_TTY_CHANNELS);
+	if (!channel_driver)
+		return -ENOMEM;
+
+	channel_driver->owner = THIS_MODULE;
+	channel_driver->driver_name = "ttyDA";
+	channel_driver->name = "ttyDA";
+	channel_driver->major = DA_TTY_MAJOR;
+	channel_driver->minor_start = 0;
+	channel_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	channel_driver->subtype = SERIAL_TYPE_NORMAL;
+	channel_driver->init_termios = tty_std_termios;
+	channel_driver->init_termios.c_cflag =
+	    B38400 | CS8 | CREAD | HUPCL | CLOCAL;
+	channel_driver->flags = TTY_DRIVER_REAL_RAW;
+
+	tty_set_operations(channel_driver, &dashtty_ops);
+
+	spin_lock_init(&dashtty_q_lock);
+	INIT_LIST_HEAD(&dashtty_output_queue);
+	init_waitqueue_head(&dashtty_waitqueue);
+
+	dashtty_thread = kthread_create(put_data, NULL, "ttyDA");
+	if (IS_ERR(dashtty_thread)) {
+		pr_info("Couldn't create dashtty thread\n");
+		return PTR_ERR(dashtty_thread);
+	}
+	kthread_bind(dashtty_thread, 0);
+	wake_up_process(dashtty_thread);
+
+	ret = tty_register_driver(channel_driver);
+
+	if (ret < 0) {
+		pr_info("Couldn't install dashtty driver: err %d\n",
+			ret);
+		put_tty_driver(channel_driver);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void dashtty_exit(void)
+{
+	kthread_stop(dashtty_thread);
+	del_timer(&poll_timer);
+	tty_unregister_driver(channel_driver);
+	put_tty_driver(channel_driver);
+}
+
+module_init(dashtty_init);
+module_exit(dashtty_exit);
+
+#ifdef CONFIG_DA_CONSOLE
+
+static void dash_console_write(struct console *co, const char *s,
+			       unsigned int count)
+{
+	int actually_written;
+
+	chancall(WRBUF, CONSOLE_CHANNEL, count, (void *)s, &actually_written);
+}
+
+static struct tty_driver *dash_console_device(struct console *c, int *index)
+{
+	*index = c->index;
+	return channel_driver;
+}
+
+struct console dash_console = {
+	.name = "ttyDA",
+	.write = dash_console_write,
+	.device = dash_console_device,
+	.flags = CON_PRINTBUFFER,
+	.index = 1,
+};
+
+#endif
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
@ 2012-10-31 16:14   ` James Hogan
  2012-10-31 16:13 ` [RFC PATCH v1 02/40] asm-generic/unistd.h: handle symbol prefixes in cond_syscall James Hogan
                     ` (39 subsequent siblings)
  40 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan, Alexander Viro, linux-fsdevel

Add the Metag Debug Adapter File System (DAFS), which uses Meta SWITCH
operations to communicate with a file server on a host computer via a
JTAG debug adapter.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
---
 MAINTAINERS        |    1 +
 arch/metag/Kconfig |    1 +
 fs/Kconfig         |    1 +
 fs/Makefile        |    1 +
 fs/dafs/Kconfig    |    6 +
 fs/dafs/Makefile   |    7 +
 fs/dafs/dafs.h     |   80 +++++
 fs/dafs/inode.c    |  830 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 927 insertions(+), 0 deletions(-)
 create mode 100644 fs/dafs/Kconfig
 create mode 100644 fs/dafs/Makefile
 create mode 100644 fs/dafs/dafs.h
 create mode 100644 fs/dafs/inode.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 1e55a5e..02e9d3e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4538,6 +4538,7 @@ F:	arch/metag/
 F:	Documentation/metag/
 F:	Documentation/devicetree/bindings/metag/
 F:	drivers/tty/metag_da.c
+F:	fs/dafs/
 
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index 96e9fbf..a186f3c 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -224,6 +224,7 @@ config METAG_DA
 	  This enables support for services provided by DA JTAG debug adapters,
 	  such as:
 	  - communication over DA channels (such as the console driver).
+	  - use of the DA filesystem.
 
 menu "Boot options"
 
diff --git a/fs/Kconfig b/fs/Kconfig
index f95ae3a..342cd55 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -220,6 +220,7 @@ source "fs/pstore/Kconfig"
 source "fs/sysv/Kconfig"
 source "fs/ufs/Kconfig"
 source "fs/exofs/Kconfig"
+source "fs/dafs/Kconfig"
 
 endif # MISC_FILESYSTEMS
 
diff --git a/fs/Makefile b/fs/Makefile
index 2fb9779..e803b53 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -125,3 +125,4 @@ obj-$(CONFIG_GFS2_FS)           += gfs2/
 obj-y				+= exofs/ # Multiple modules
 obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
+obj-$(CONFIG_DAFS_FS)           += dafs/
diff --git a/fs/dafs/Kconfig b/fs/dafs/Kconfig
new file mode 100644
index 0000000..2ead46f
--- /dev/null
+++ b/fs/dafs/Kconfig
@@ -0,0 +1,6 @@
+config DAFS_FS
+	bool "Meta DA filesystem support"
+	depends on METAG_DA
+	help
+	  This enables the DA filesystem, which allows Linux to
+	  to access files on a system attached via a debug adapter.
diff --git a/fs/dafs/Makefile b/fs/dafs/Makefile
new file mode 100644
index 0000000..479ce8f
--- /dev/null
+++ b/fs/dafs/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for DAfs the Debug Adapter filesystem.
+#
+
+obj-$(CONFIG_DAFS_FS) += dafs.o
+
+dafs-objs := inode.o
diff --git a/fs/dafs/dafs.h b/fs/dafs/dafs.h
new file mode 100644
index 0000000..2352f91
--- /dev/null
+++ b/fs/dafs/dafs.h
@@ -0,0 +1,80 @@
+#ifndef _DAFS_H_
+#define _DAFS_H_
+
+#define DA_OP_OPEN 0
+#define DA_OP_CREAT 1
+#define DA_OP_READ 2
+#define DA_OP_WRITE 3
+#define DA_OP_CLOSE 4
+#define DA_OP_LINK 5
+#define DA_OP_LSEEK 6
+#define DA_OP_UNLINK 7
+#define DA_OP_ISATTY 8
+#define DA_OP_FCNTL 9
+#define DA_OP_STAT 10
+#define DA_OP_FSTAT 11
+#define DA_OP_GETCWD 12
+#define DA_OP_CHDIR 13
+#define DA_OP_MKDIR 14
+#define DA_OP_RMDIR 15
+#define DA_OP_FINDFIRST 16
+#define DA_OP_FINDNEXT 17
+#define DA_OP_FINDCLOSE 18
+#define DA_OP_CHMOD 19
+#define DA_OP_PREAD 20
+#define DA_OP_PWRITE 21
+
+#define OS_TYPE_FILE 1
+#define OS_TYPE_DIR 2
+#define OS_TYPE_SYMLINK 3
+#define OS_TYPE_CHARDEV 4
+#define OS_TYPE_BLOCKDEV 5
+#define OS_TYPE_FIFO 6
+#define OS_TYPE_SOCK 7
+
+#define DA_O_RDONLY 0
+#define DA_O_WRONLY 1
+#define DA_O_RDWR 2
+#define DA_O_APPEND 8
+#define DA_O_CREAT 0x0200
+#define DA_O_TRUNC 0x0400
+#define DA_O_EXCL 0x0800
+
+#define DA_O_AFFINITY_THREAD_0 0x10000
+#define DA_O_AFFINITY_THREAD_1 0x20000
+#define DA_O_AFFINITY_THREAD_2 0x40000
+#define DA_O_AFFINITY_THREAD_3 0x80000
+#define DA_O_AFFINITY_SHIFT 16
+
+#define DA_S_IWUSR	0200	/* 0x80 */
+#define DA_S_IRUSR	0400	/* 0x100 */
+
+struct da_stat {
+	short st_dev;
+	unsigned short st_ino;
+	unsigned st_mode;
+	unsigned short st_nlink;
+	unsigned short st_uid;
+	unsigned short st_gid;
+	short st_rdev;
+	int st_size;
+	int st_atime;
+	int st_spare1;
+	int st_mtime;
+	int st_spare2;
+	int st_ctime;
+	int st_spare3;
+	int st_blksize;
+	int st_blocks;
+	int st_spare4[2];
+};
+
+#define _A_SUBDIR 0x10
+
+struct da_finddata {
+	unsigned long size;
+	unsigned long attrib;
+	char name[260];
+};
+
+#endif
diff --git a/fs/dafs/inode.c b/fs/dafs/inode.c
new file mode 100644
index 0000000..25e0ca8
--- /dev/null
+++ b/fs/dafs/inode.c
@@ -0,0 +1,830 @@
+/*
+ * Copyright (C) 2008,2009,2010 Imagination Technologies Ltd.
+ * Licensed under the GPL
+ *
+ * Based on hostfs for UML.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/slab.h>
+
+#include <asm/da.h>
+#include <asm/hwthread.h>
+
+#include "dafs.h"
+
+static int fserrno;
+
+int fscall(int system_call, int arg1, int arg2, int arg3, int arg4, int arg5)
+{
+	register int arg1_           __asm__("D1Ar1") = arg1;
+	register int arg2_           __asm__("D0Ar2") = arg2;
+	register int arg3_           __asm__("D1Ar3") = arg3;
+	register int arg4_           __asm__("D0Ar4") = arg4;
+	register int arg5_           __asm__("D1Ar5") = arg5;
+	register int system_call_    __asm__("D0Ar6") = system_call;
+	register int result          __asm__("D0Re0");
+	register int errno           __asm__("D1Re0");
+
+	__asm__ volatile (
+		"SETL   [A0StP++], %7,%6\n\t"
+		"SETL   [A0StP++], %5,%4\n\t"
+		"SETL   [A0StP++], %3,%2\n\t"
+		"ADD    A0StP, A0StP, #8\n\t"
+		"SWITCH #0x0C00208\n\t"
+		"GETL   %0, %1, [A0StP+#-8]\n\t"
+		"SUB    A0StP, A0StP, #(4*6)+8\n\t"
+		: "=r" (result), "=r" (errno)
+		: "r" (arg1_), "r" (arg2_), "r" (arg3_),
+		  "r" (arg4_), "r" (arg5_), "r" (system_call_)
+		: "memory");
+
+	fserrno = errno;
+
+	return result;
+}
+
+struct dafs_inode_info {
+	int fd;
+	int mode;
+	struct inode vfs_inode;
+};
+
+static inline struct dafs_inode_info *DAFS_I(struct inode *inode)
+{
+	return container_of(inode, struct dafs_inode_info, vfs_inode);
+}
+
+#define FILE_DAFS_I(file) DAFS_I((file)->f_path.dentry->d_inode)
+
+static int dafs_d_delete(const struct dentry *dentry)
+{
+	return 1;
+}
+
+static const struct dentry_operations dafs_dentry_ops = {
+	.d_delete		= dafs_d_delete,
+};
+
+#define DAFS_SUPER_MAGIC 0xdadadaf5
+
+static const struct inode_operations dafs_iops;
+static const struct inode_operations dafs_dir_iops;
+
+static char *__dentry_name(struct dentry *dentry, char *name)
+{
+	char *p = dentry_path_raw(dentry, name, PATH_MAX);
+	char *root;
+	size_t len;
+
+	root = dentry->d_sb->s_fs_info;
+	len = strlen(root);
+	if (IS_ERR(p)) {
+		__putname(name);
+		return NULL;
+	}
+
+	strlcpy(name, root, PATH_MAX);
+	if (len > p - name) {
+		__putname(name);
+		return NULL;
+	}
+	if (p > name + len) {
+		char *s = name + len;
+		while ((*s++ = *p++) != '\0')
+			;
+	}
+	return name;
+}
+
+static char *dentry_name(struct dentry *dentry)
+{
+	char *name = __getname();
+	if (!name)
+		return NULL;
+
+	return __dentry_name(dentry, name); /* will unlock */
+}
+
+static int stat_file(const char *path, struct da_stat *p, int fd)
+{
+	int ret;
+	memset(p, 0, sizeof(*p));
+
+	if (fd >= 0) {
+		ret = fscall(DA_OP_FSTAT, fd, (int)p, 0, 0, 0);
+		if (ret < 0) {
+			/* Some versions of Codescape do not fill out errno. */
+			if (ret < 0 && fserrno == 0)
+				fserrno = ENOENT;
+			return -fserrno;
+		}
+	} else {
+		ret = fscall(DA_OP_STAT, (int)path, (int)p, strlen(path), 0, 0);
+		if (ret < 0) {
+			/* Some versions of Codescape do not fill out errno. */
+			if (ret < 0 && fserrno == 0)
+				fserrno = ENOENT;
+			return -fserrno;
+		}
+	}
+
+	return 0;
+}
+
+static struct inode *dafs_iget(struct super_block *sb)
+{
+	struct inode *inode = new_inode(sb);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+	return inode;
+}
+
+static struct inode *dafs_alloc_inode(struct super_block *sb)
+{
+	struct dafs_inode_info *hi;
+
+	hi = kzalloc(sizeof(*hi), GFP_KERNEL);
+	if (hi == NULL)
+		return NULL;
+
+	hi->fd = -1;
+	inode_init_once(&hi->vfs_inode);
+	return &hi->vfs_inode;
+}
+
+static void close_file(void *stream)
+{
+	int fd = *((int *) stream);
+
+	fscall(DA_OP_CLOSE, fd, 0, 0, 0, 0);
+}
+
+static void dafs_evict_inode(struct inode *inode)
+{
+	truncate_inode_pages(&inode->i_data, 0);
+	clear_inode(inode);
+	if (DAFS_I(inode)->fd != -1) {
+		close_file(&DAFS_I(inode)->fd);
+		DAFS_I(inode)->fd = -1;
+	}
+}
+
+static void dafs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	kfree(DAFS_I(inode));
+}
+
+static void dafs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, dafs_i_callback);
+}
+
+static const struct super_operations dafs_sbops = {
+	.alloc_inode	= dafs_alloc_inode,
+	.drop_inode	= generic_delete_inode,
+	.evict_inode	= dafs_evict_inode,
+	.destroy_inode	= dafs_destroy_inode,
+};
+
+static int open_dir(char *path, struct da_finddata *finddata)
+{
+	int len = strlen(path);
+	char buf[len + 3];
+
+	strcpy(buf, path);
+	if (buf[len - 1] != '/')
+		strcat(buf, "/*");
+	else
+		strcat(buf, "*");
+
+	return fscall(DA_OP_FINDFIRST, (int)buf, (int)finddata, 0, 0, 0);
+}
+
+static void close_dir(int handle)
+{
+	fscall(DA_OP_FINDCLOSE, handle, 0, 0, 0, 0);
+}
+
+static int read_dir(int handle, struct da_finddata *finddata)
+{
+	return fscall(DA_OP_FINDNEXT, handle, (int)finddata, 0, 0, 0);
+}
+
+static int dafs_readdir(struct file *file, void *ent, filldir_t filldir)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	char *name;
+	int handle;
+	unsigned long long next, ino;
+	int error = 0;
+	struct da_finddata finddata;
+
+	name = dentry_name(file->f_path.dentry);
+	if (name == NULL)
+		return -ENOMEM;
+	handle = open_dir(name, &finddata);
+	__putname(name);
+	if (handle == -1)
+		return -fserrno;
+
+	next = 1;
+
+	if (file->f_pos == 0) {
+		error = (*filldir)(ent, ".", file->f_pos + 1,
+				   file->f_pos, inode->i_ino,
+				   DT_DIR);
+		if (error < 0)
+			goto out;
+		file->f_pos++;
+	}
+
+	while (1) {
+		error = read_dir(handle, &finddata);
+		if (error)
+			break;
+
+		if (next >= file->f_pos) {
+			size_t len = strlen(finddata.name);
+			ino = iunique(sb, 100);
+			error = (*filldir)(ent, finddata.name, len,
+					   file->f_pos, ino,
+					   (finddata.attrib & _A_SUBDIR) ?
+					    DT_DIR : DT_REG);
+			if (error)
+				break;
+			file->f_pos++;
+		}
+		next++;
+	}
+out:
+	close_dir(handle);
+	return 0;
+}
+
+static int dafs_file_open(struct inode *ino, struct file *file)
+{
+	static DEFINE_MUTEX(open_mutex);
+	char *name;
+	int mode, fmode, flags = 0, r = 0, w = 0, fd;
+	int i;
+
+	fmode = file->f_mode & (FMODE_READ | FMODE_WRITE);
+	if ((fmode & DAFS_I(ino)->mode) == fmode)
+		return 0;
+
+	mode = ino->i_mode & (DA_S_IWUSR | DA_S_IRUSR);
+
+	mode |= DAFS_I(ino)->mode;
+
+	DAFS_I(ino)->mode |= fmode;
+	if (DAFS_I(ino)->mode & FMODE_READ)
+		r = 1;
+	if (DAFS_I(ino)->mode & FMODE_WRITE) {
+		w = 1;
+		r = 1;
+	}
+
+retry:
+	if (r && !w)
+		flags |= DA_O_RDONLY;
+	else if (!r && w)
+		flags |= DA_O_WRONLY;
+	else if (r && w)
+		flags |= DA_O_RDWR;
+
+	if (file->f_flags & O_CREAT)
+		flags |= DA_O_CREAT;
+
+	if (file->f_flags & O_TRUNC)
+		flags |= DA_O_TRUNC;
+
+	/*
+	 * Set the affinity for this file handle to all CPUs. If we
+	 * don't do this then, if the process that opened the file
+	 * migrates to a different cpu, the FileServer will not accept
+	 * the file handle.
+	 */
+	for (i = 0; i < NR_CPUS; i++) {
+		u8 hwthread = cpu_2_hwthread_id[i];
+		flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread));
+	}
+
+	name = dentry_name(file->f_path.dentry);
+	if (name == NULL)
+		return -ENOMEM;
+
+	fd = fscall(DA_OP_OPEN, (int)name, flags, mode, strlen(name), 0);
+	__putname(name);
+	if (fd < 0)
+		return fd;
+
+	mutex_lock(&open_mutex);
+	/* somebody else had handled it first? */
+	if ((mode & DAFS_I(ino)->mode) == mode) {
+		mutex_unlock(&open_mutex);
+		return 0;
+	}
+	if ((mode | DAFS_I(ino)->mode) != mode) {
+		mode |= DAFS_I(ino)->mode;
+		mutex_unlock(&open_mutex);
+		close_file(&fd);
+		goto retry;
+	}
+	DAFS_I(ino)->fd = fd;
+	DAFS_I(ino)->mode = mode;
+	mutex_unlock(&open_mutex);
+
+	return 0;
+}
+
+static const struct file_operations dafs_file_fops = {
+	.llseek		= generic_file_llseek,
+	.read		= do_sync_read,
+	.splice_read	= generic_file_splice_read,
+	.aio_read	= generic_file_aio_read,
+	.aio_write	= generic_file_aio_write,
+	.write		= do_sync_write,
+	.mmap		= generic_file_mmap,
+	.open		= dafs_file_open,
+	.release	= NULL,
+};
+
+static const struct file_operations dafs_dir_fops = {
+	.llseek		= generic_file_llseek,
+	.readdir	= dafs_readdir,
+	.read		= generic_read_dir,
+};
+
+static int read_file(int fd, unsigned long long *offset, const char *buf,
+		     int len)
+{
+	int n;
+
+	n = fscall(DA_OP_PREAD, fd, (int)buf, len, (int)*offset, 0);
+
+	if (n < 0)
+		return -fserrno;
+
+	return n;
+}
+
+static int write_file(int fd, unsigned long long *offset, const char *buf,
+		      int len)
+{
+	int n;
+
+	n = fscall(DA_OP_PWRITE, fd, (int)buf, len, (int)*offset, 0);
+
+	if (n < 0)
+		return -fserrno;
+
+	return n;
+}
+
+static int dafs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode = mapping->host;
+	char *buffer;
+	unsigned long long base;
+	int count = PAGE_CACHE_SIZE;
+	int end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+	int err;
+
+	if (page->index >= end_index)
+		count = inode->i_size & (PAGE_CACHE_SIZE-1);
+
+	buffer = kmap(page);
+	base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;
+
+	err = write_file(DAFS_I(inode)->fd, &base, buffer, count);
+	if (err != count) {
+		ClearPageUptodate(page);
+		goto out;
+	}
+
+	if (base > inode->i_size)
+		inode->i_size = base;
+
+	if (PageError(page))
+		ClearPageError(page);
+	err = 0;
+
+ out:
+	kunmap(page);
+
+	unlock_page(page);
+	return err;
+}
+
+static int dafs_readpage(struct file *file, struct page *page)
+{
+	char *buffer;
+	long long start;
+	int err = 0;
+
+	start = (long long) page->index << PAGE_CACHE_SHIFT;
+	buffer = kmap(page);
+	err = read_file(FILE_DAFS_I(file)->fd, &start, buffer,
+			PAGE_CACHE_SIZE);
+	if (err < 0)
+		goto out;
+
+	memset(&buffer[err], 0, PAGE_CACHE_SIZE - err);
+
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	if (PageError(page))
+		ClearPageError(page);
+	err = 0;
+ out:
+	kunmap(page);
+	unlock_page(page);
+	return err;
+}
+
+int dafs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+
+	*pagep = grab_cache_page_write_begin(mapping, index, flags);
+	if (!*pagep)
+		return -ENOMEM;
+	return 0;
+}
+
+int dafs_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
+{
+	struct inode *inode = mapping->host;
+	void *buffer;
+	unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+	int err;
+
+	buffer = kmap(page);
+	err = write_file(FILE_DAFS_I(file)->fd, &pos, buffer + from, copied);
+	kunmap(page);
+
+	if (!PageUptodate(page) && err == PAGE_CACHE_SIZE)
+		SetPageUptodate(page);
+
+	/*
+	 * If err > 0, write_file has added err to pos, so we are comparing
+	 * i_size against the last byte written.
+	 */
+	if (err > 0 && (pos > inode->i_size))
+		inode->i_size = pos;
+	unlock_page(page);
+	page_cache_release(page);
+
+	return err;
+}
+
+static const struct address_space_operations dafs_aops = {
+	.writepage	= dafs_writepage,
+	.readpage	= dafs_readpage,
+	.set_page_dirty	= __set_page_dirty_nobuffers,
+	.write_begin	= dafs_write_begin,
+	.write_end	= dafs_write_end,
+};
+
+static int read_name(struct inode *ino, char *name)
+{
+	dev_t rdev;
+	struct da_stat st;
+	int err = stat_file(name, &st, -1);
+	if (err)
+		return err;
+
+	/* No valid maj and min from DA.*/
+	rdev = MKDEV(0, 0);
+
+	switch (st.st_mode & S_IFMT) {
+	case S_IFDIR:
+		ino->i_op = &dafs_dir_iops;
+		ino->i_fop = &dafs_dir_fops;
+		break;
+	case S_IFCHR:
+	case S_IFBLK:
+	case S_IFIFO:
+	case S_IFSOCK:
+		init_special_inode(ino, st.st_mode & S_IFMT, rdev);
+		ino->i_op = &dafs_iops;
+		break;
+
+	case S_IFLNK:
+	default:
+		ino->i_op = &dafs_iops;
+		ino->i_fop = &dafs_file_fops;
+		ino->i_mapping->a_ops = &dafs_aops;
+	}
+
+	ino->i_ino = st.st_ino;
+	ino->i_mode = st.st_mode;
+	set_nlink(ino, st.st_nlink);
+
+	ino->i_uid = st.st_uid;
+	ino->i_gid = st.st_gid;
+	ino->i_atime.tv_sec = st.st_atime;
+	ino->i_atime.tv_nsec = 0;
+	ino->i_mtime.tv_sec = st.st_mtime;
+	ino->i_mtime.tv_nsec = 0;
+	ino->i_ctime.tv_sec = st.st_ctime;
+	ino->i_ctime.tv_nsec = 0;
+	ino->i_size = st.st_size;
+	ino->i_blocks = st.st_blocks;
+	return 0;
+}
+
+static int dafs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+		       bool excl)
+{
+	struct inode *inode;
+	char *name;
+	int error, fd;
+	int damode;
+	int creat_flags = DA_O_TRUNC | DA_O_CREAT | DA_O_WRONLY;
+	int i;
+
+	inode = dafs_iget(dir->i_sb);
+	if (IS_ERR(inode)) {
+		error = PTR_ERR(inode);
+		goto out;
+	}
+
+	damode = mode & (DA_S_IWUSR | DA_S_IRUSR);
+
+	error = -ENOMEM;
+	name = dentry_name(dentry);
+	if (name == NULL)
+		goto out_put;
+
+	/*
+	 * creat() will only create text mode files on a Windows host
+	 * at present.  Replicate the creat() functionality with an
+	 * open() call, which always creates binary files. Set the
+	 * affinity to all hardware threads.
+	 */
+	for (i = 0; i < NR_CPUS; i++) {
+		u8 hwthread = cpu_2_hwthread_id[i];
+		creat_flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread));
+	}
+
+	fd = fscall(DA_OP_OPEN, (int)name, creat_flags, damode, strlen(name),
+		    0);
+	if (fd < 0)
+		error = fd;
+	else
+		error = read_name(inode, name);
+
+	kfree(name);
+	if (error)
+		goto out_put;
+
+	DAFS_I(inode)->fd = fd;
+	DAFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
+	d_instantiate(dentry, inode);
+	return 0;
+
+ out_put:
+	iput(inode);
+ out:
+	return error;
+}
+
+static struct dentry *dafs_lookup(struct inode *ino, struct dentry *dentry,
+				  unsigned int flags)
+{
+	struct inode *inode;
+	char *name;
+	int err;
+
+	inode = dafs_iget(ino->i_sb);
+	if (IS_ERR(inode)) {
+		err = PTR_ERR(inode);
+		goto out;
+	}
+
+	err = -ENOMEM;
+	name = dentry_name(dentry);
+	if (name == NULL)
+		goto out_put;
+
+	err = read_name(inode, name);
+
+	__putname(name);
+	if (err == -ENOENT) {
+		iput(inode);
+		inode = NULL;
+	} else if (err)
+		goto out_put;
+
+	d_add(dentry, inode);
+	return NULL;
+
+ out_put:
+	iput(inode);
+ out:
+	return ERR_PTR(err);
+}
+
+int dafs_link(struct dentry *to, struct inode *ino, struct dentry *from)
+{
+	char *from_name, *to_name;
+	int err;
+
+	from_name = dentry_name(from);
+	if (from_name == NULL)
+		return -ENOMEM;
+	to_name = dentry_name(to);
+	if (to_name == NULL) {
+		__putname(from_name);
+		return -ENOMEM;
+	}
+	err = -EINVAL;
+	__putname(from_name);
+	__putname(to_name);
+	return err;
+}
+
+int dafs_unlink(struct inode *ino, struct dentry *dentry)
+{
+	char *file;
+	int err;
+
+	file = dentry_name(dentry);
+	if (file == NULL)
+		return -ENOMEM;
+
+	err = fscall(DA_OP_UNLINK, (int)file, 0, 0, 0, 0);
+	__putname(file);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+static int do_mkdir(const char *file, int mode)
+{
+	int err;
+
+	err = fscall(DA_OP_MKDIR, (int)file, mode, strlen(file), 0, 0);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+int dafs_mkdir(struct inode *ino, struct dentry *dentry, umode_t mode)
+{
+	char *file;
+	int err;
+
+	file = dentry_name(dentry);
+	if (file == NULL)
+		return -ENOMEM;
+	err = do_mkdir(file, mode);
+	__putname(file);
+	return err;
+}
+
+static int do_rmdir(const char *file)
+{
+	int err;
+
+	err = fscall(DA_OP_RMDIR, (int)file, strlen(file), 0, 0, 0);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+int dafs_rmdir(struct inode *ino, struct dentry *dentry)
+{
+	char *file;
+	int err;
+
+	file = dentry_name(dentry);
+	if (file == NULL)
+		return -ENOMEM;
+	err = do_rmdir(file);
+	__putname(file);
+	return err;
+}
+
+int dafs_rename(struct inode *from_ino, struct dentry *from,
+		  struct inode *to_ino, struct dentry *to)
+{
+	char *from_name, *to_name;
+	int err;
+
+	from_name = dentry_name(from);
+	if (from_name == NULL)
+		return -ENOMEM;
+	to_name = dentry_name(to);
+	if (to_name == NULL) {
+		__putname(from_name);
+		return -ENOMEM;
+	}
+	err = -EINVAL;
+	__putname(from_name);
+	__putname(to_name);
+	return err;
+}
+
+static const struct inode_operations dafs_iops = {
+	.create		= dafs_create,
+	.link		= dafs_link,
+	.unlink		= dafs_unlink,
+	.mkdir		= dafs_mkdir,
+	.rmdir		= dafs_rmdir,
+	.rename		= dafs_rename,
+};
+
+static const struct inode_operations dafs_dir_iops = {
+	.create		= dafs_create,
+	.lookup		= dafs_lookup,
+	.link		= dafs_link,
+	.unlink		= dafs_unlink,
+	.mkdir		= dafs_mkdir,
+	.rmdir		= dafs_rmdir,
+	.rename		= dafs_rename,
+};
+
+static char *host_root_path = ".";
+
+static int dafs_fill_sb_common(struct super_block *sb, void *d, int silent)
+{
+	struct inode *root_inode;
+	int err;
+
+	sb->s_blocksize = 1024;
+	sb->s_blocksize_bits = 10;
+	sb->s_magic = DAFS_SUPER_MAGIC;
+	sb->s_op = &dafs_sbops;
+	sb->s_d_op = &dafs_dentry_ops;
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+	err = -ENOMEM;
+
+	root_inode = new_inode(sb);
+	if (!root_inode)
+		goto out;
+
+	err = read_name(root_inode, host_root_path);
+	if (err)
+		goto out_put;
+
+	err = -ENOMEM;
+	sb->s_fs_info = host_root_path;
+	sb->s_root = d_make_root(root_inode);
+	if (sb->s_root == NULL)
+		goto out;
+
+	return 0;
+
+out_put:
+	iput(root_inode);
+out:
+	return err;
+}
+
+static struct dentry *dafs_read_sb(struct file_system_type *type,
+				   int flags, const char *dev_name,
+				   void *data)
+{
+	if (!metag_da_enabled())
+		return ERR_PTR(-ENODEV);
+	return mount_nodev(type, flags, data, dafs_fill_sb_common);
+}
+
+static struct file_system_type dafs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "dafs",
+	.mount		= dafs_read_sb,
+	.kill_sb	= kill_anon_super,
+	.fs_flags	= 0,
+};
+
+static int __init init_dafs(void)
+{
+	return register_filesystem(&dafs_type);
+}
+
+static void __exit exit_dafs(void)
+{
+	unregister_filesystem(&dafs_type);
+}
+
+module_init(init_dafs)
+module_exit(exit_dafs)
+MODULE_LICENSE("GPL");
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag
@ 2012-10-31 16:14   ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:14 UTC (permalink / raw)
  To: linux-arch; +Cc: James Hogan, Alexander Viro, linux-fsdevel

Add the Metag Debug Adapter File System (DAFS), which uses Meta SWITCH
operations to communicate with a file server on a host computer via a
JTAG debug adapter.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
---
 MAINTAINERS        |    1 +
 arch/metag/Kconfig |    1 +
 fs/Kconfig         |    1 +
 fs/Makefile        |    1 +
 fs/dafs/Kconfig    |    6 +
 fs/dafs/Makefile   |    7 +
 fs/dafs/dafs.h     |   80 +++++
 fs/dafs/inode.c    |  830 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 927 insertions(+), 0 deletions(-)
 create mode 100644 fs/dafs/Kconfig
 create mode 100644 fs/dafs/Makefile
 create mode 100644 fs/dafs/dafs.h
 create mode 100644 fs/dafs/inode.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 1e55a5e..02e9d3e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4538,6 +4538,7 @@ F:	arch/metag/
 F:	Documentation/metag/
 F:	Documentation/devicetree/bindings/metag/
 F:	drivers/tty/metag_da.c
+F:	fs/dafs/
 
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index 96e9fbf..a186f3c 100644
--- a/arch/metag/Kconfig
+++ b/arch/metag/Kconfig
@@ -224,6 +224,7 @@ config METAG_DA
 	  This enables support for services provided by DA JTAG debug adapters,
 	  such as:
 	  - communication over DA channels (such as the console driver).
+	  - use of the DA filesystem.
 
 menu "Boot options"
 
diff --git a/fs/Kconfig b/fs/Kconfig
index f95ae3a..342cd55 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -220,6 +220,7 @@ source "fs/pstore/Kconfig"
 source "fs/sysv/Kconfig"
 source "fs/ufs/Kconfig"
 source "fs/exofs/Kconfig"
+source "fs/dafs/Kconfig"
 
 endif # MISC_FILESYSTEMS
 
diff --git a/fs/Makefile b/fs/Makefile
index 2fb9779..e803b53 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -125,3 +125,4 @@ obj-$(CONFIG_GFS2_FS)           += gfs2/
 obj-y				+= exofs/ # Multiple modules
 obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
+obj-$(CONFIG_DAFS_FS)           += dafs/
diff --git a/fs/dafs/Kconfig b/fs/dafs/Kconfig
new file mode 100644
index 0000000..2ead46f
--- /dev/null
+++ b/fs/dafs/Kconfig
@@ -0,0 +1,6 @@
+config DAFS_FS
+	bool "Meta DA filesystem support"
+	depends on METAG_DA
+	help
+	  This enables the DA filesystem, which allows Linux to
+	  to access files on a system attached via a debug adapter.
diff --git a/fs/dafs/Makefile b/fs/dafs/Makefile
new file mode 100644
index 0000000..479ce8f
--- /dev/null
+++ b/fs/dafs/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for DAfs the Debug Adapter filesystem.
+#
+
+obj-$(CONFIG_DAFS_FS) += dafs.o
+
+dafs-objs := inode.o
diff --git a/fs/dafs/dafs.h b/fs/dafs/dafs.h
new file mode 100644
index 0000000..2352f91
--- /dev/null
+++ b/fs/dafs/dafs.h
@@ -0,0 +1,80 @@
+#ifndef _DAFS_H_
+#define _DAFS_H_
+
+#define DA_OP_OPEN 0
+#define DA_OP_CREAT 1
+#define DA_OP_READ 2
+#define DA_OP_WRITE 3
+#define DA_OP_CLOSE 4
+#define DA_OP_LINK 5
+#define DA_OP_LSEEK 6
+#define DA_OP_UNLINK 7
+#define DA_OP_ISATTY 8
+#define DA_OP_FCNTL 9
+#define DA_OP_STAT 10
+#define DA_OP_FSTAT 11
+#define DA_OP_GETCWD 12
+#define DA_OP_CHDIR 13
+#define DA_OP_MKDIR 14
+#define DA_OP_RMDIR 15
+#define DA_OP_FINDFIRST 16
+#define DA_OP_FINDNEXT 17
+#define DA_OP_FINDCLOSE 18
+#define DA_OP_CHMOD 19
+#define DA_OP_PREAD 20
+#define DA_OP_PWRITE 21
+
+#define OS_TYPE_FILE 1
+#define OS_TYPE_DIR 2
+#define OS_TYPE_SYMLINK 3
+#define OS_TYPE_CHARDEV 4
+#define OS_TYPE_BLOCKDEV 5
+#define OS_TYPE_FIFO 6
+#define OS_TYPE_SOCK 7
+
+#define DA_O_RDONLY 0
+#define DA_O_WRONLY 1
+#define DA_O_RDWR 2
+#define DA_O_APPEND 8
+#define DA_O_CREAT 0x0200
+#define DA_O_TRUNC 0x0400
+#define DA_O_EXCL 0x0800
+
+#define DA_O_AFFINITY_THREAD_0 0x10000
+#define DA_O_AFFINITY_THREAD_1 0x20000
+#define DA_O_AFFINITY_THREAD_2 0x40000
+#define DA_O_AFFINITY_THREAD_3 0x80000
+#define DA_O_AFFINITY_SHIFT 16
+
+#define DA_S_IWUSR	0200	/* 0x80 */
+#define DA_S_IRUSR	0400	/* 0x100 */
+
+struct da_stat {
+	short st_dev;
+	unsigned short st_ino;
+	unsigned st_mode;
+	unsigned short st_nlink;
+	unsigned short st_uid;
+	unsigned short st_gid;
+	short st_rdev;
+	int st_size;
+	int st_atime;
+	int st_spare1;
+	int st_mtime;
+	int st_spare2;
+	int st_ctime;
+	int st_spare3;
+	int st_blksize;
+	int st_blocks;
+	int st_spare4[2];
+};
+
+#define _A_SUBDIR 0x10
+
+struct da_finddata {
+	unsigned long size;
+	unsigned long attrib;
+	char name[260];
+};
+
+#endif
diff --git a/fs/dafs/inode.c b/fs/dafs/inode.c
new file mode 100644
index 0000000..25e0ca8
--- /dev/null
+++ b/fs/dafs/inode.c
@@ -0,0 +1,830 @@
+/*
+ * Copyright (C) 2008,2009,2010 Imagination Technologies Ltd.
+ * Licensed under the GPL
+ *
+ * Based on hostfs for UML.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/slab.h>
+
+#include <asm/da.h>
+#include <asm/hwthread.h>
+
+#include "dafs.h"
+
+static int fserrno;
+
+int fscall(int system_call, int arg1, int arg2, int arg3, int arg4, int arg5)
+{
+	register int arg1_           __asm__("D1Ar1") = arg1;
+	register int arg2_           __asm__("D0Ar2") = arg2;
+	register int arg3_           __asm__("D1Ar3") = arg3;
+	register int arg4_           __asm__("D0Ar4") = arg4;
+	register int arg5_           __asm__("D1Ar5") = arg5;
+	register int system_call_    __asm__("D0Ar6") = system_call;
+	register int result          __asm__("D0Re0");
+	register int errno           __asm__("D1Re0");
+
+	__asm__ volatile (
+		"SETL   [A0StP++], %7,%6\n\t"
+		"SETL   [A0StP++], %5,%4\n\t"
+		"SETL   [A0StP++], %3,%2\n\t"
+		"ADD    A0StP, A0StP, #8\n\t"
+		"SWITCH #0x0C00208\n\t"
+		"GETL   %0, %1, [A0StP+#-8]\n\t"
+		"SUB    A0StP, A0StP, #(4*6)+8\n\t"
+		: "=r" (result), "=r" (errno)
+		: "r" (arg1_), "r" (arg2_), "r" (arg3_),
+		  "r" (arg4_), "r" (arg5_), "r" (system_call_)
+		: "memory");
+
+	fserrno = errno;
+
+	return result;
+}
+
+struct dafs_inode_info {
+	int fd;
+	int mode;
+	struct inode vfs_inode;
+};
+
+static inline struct dafs_inode_info *DAFS_I(struct inode *inode)
+{
+	return container_of(inode, struct dafs_inode_info, vfs_inode);
+}
+
+#define FILE_DAFS_I(file) DAFS_I((file)->f_path.dentry->d_inode)
+
+static int dafs_d_delete(const struct dentry *dentry)
+{
+	return 1;
+}
+
+static const struct dentry_operations dafs_dentry_ops = {
+	.d_delete		= dafs_d_delete,
+};
+
+#define DAFS_SUPER_MAGIC 0xdadadaf5
+
+static const struct inode_operations dafs_iops;
+static const struct inode_operations dafs_dir_iops;
+
+static char *__dentry_name(struct dentry *dentry, char *name)
+{
+	char *p = dentry_path_raw(dentry, name, PATH_MAX);
+	char *root;
+	size_t len;
+
+	root = dentry->d_sb->s_fs_info;
+	len = strlen(root);
+	if (IS_ERR(p)) {
+		__putname(name);
+		return NULL;
+	}
+
+	strlcpy(name, root, PATH_MAX);
+	if (len > p - name) {
+		__putname(name);
+		return NULL;
+	}
+	if (p > name + len) {
+		char *s = name + len;
+		while ((*s++ = *p++) != '\0')
+			;
+	}
+	return name;
+}
+
+static char *dentry_name(struct dentry *dentry)
+{
+	char *name = __getname();
+	if (!name)
+		return NULL;
+
+	return __dentry_name(dentry, name); /* will unlock */
+}
+
+static int stat_file(const char *path, struct da_stat *p, int fd)
+{
+	int ret;
+	memset(p, 0, sizeof(*p));
+
+	if (fd >= 0) {
+		ret = fscall(DA_OP_FSTAT, fd, (int)p, 0, 0, 0);
+		if (ret < 0) {
+			/* Some versions of Codescape do not fill out errno. */
+			if (ret < 0 && fserrno == 0)
+				fserrno = ENOENT;
+			return -fserrno;
+		}
+	} else {
+		ret = fscall(DA_OP_STAT, (int)path, (int)p, strlen(path), 0, 0);
+		if (ret < 0) {
+			/* Some versions of Codescape do not fill out errno. */
+			if (ret < 0 && fserrno == 0)
+				fserrno = ENOENT;
+			return -fserrno;
+		}
+	}
+
+	return 0;
+}
+
+static struct inode *dafs_iget(struct super_block *sb)
+{
+	struct inode *inode = new_inode(sb);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+	return inode;
+}
+
+static struct inode *dafs_alloc_inode(struct super_block *sb)
+{
+	struct dafs_inode_info *hi;
+
+	hi = kzalloc(sizeof(*hi), GFP_KERNEL);
+	if (hi == NULL)
+		return NULL;
+
+	hi->fd = -1;
+	inode_init_once(&hi->vfs_inode);
+	return &hi->vfs_inode;
+}
+
+static void close_file(void *stream)
+{
+	int fd = *((int *) stream);
+
+	fscall(DA_OP_CLOSE, fd, 0, 0, 0, 0);
+}
+
+static void dafs_evict_inode(struct inode *inode)
+{
+	truncate_inode_pages(&inode->i_data, 0);
+	clear_inode(inode);
+	if (DAFS_I(inode)->fd != -1) {
+		close_file(&DAFS_I(inode)->fd);
+		DAFS_I(inode)->fd = -1;
+	}
+}
+
+static void dafs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	kfree(DAFS_I(inode));
+}
+
+static void dafs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, dafs_i_callback);
+}
+
+static const struct super_operations dafs_sbops = {
+	.alloc_inode	= dafs_alloc_inode,
+	.drop_inode	= generic_delete_inode,
+	.evict_inode	= dafs_evict_inode,
+	.destroy_inode	= dafs_destroy_inode,
+};
+
+static int open_dir(char *path, struct da_finddata *finddata)
+{
+	int len = strlen(path);
+	char buf[len + 3];
+
+	strcpy(buf, path);
+	if (buf[len - 1] != '/')
+		strcat(buf, "/*");
+	else
+		strcat(buf, "*");
+
+	return fscall(DA_OP_FINDFIRST, (int)buf, (int)finddata, 0, 0, 0);
+}
+
+static void close_dir(int handle)
+{
+	fscall(DA_OP_FINDCLOSE, handle, 0, 0, 0, 0);
+}
+
+static int read_dir(int handle, struct da_finddata *finddata)
+{
+	return fscall(DA_OP_FINDNEXT, handle, (int)finddata, 0, 0, 0);
+}
+
+static int dafs_readdir(struct file *file, void *ent, filldir_t filldir)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	char *name;
+	int handle;
+	unsigned long long next, ino;
+	int error = 0;
+	struct da_finddata finddata;
+
+	name = dentry_name(file->f_path.dentry);
+	if (name == NULL)
+		return -ENOMEM;
+	handle = open_dir(name, &finddata);
+	__putname(name);
+	if (handle == -1)
+		return -fserrno;
+
+	next = 1;
+
+	if (file->f_pos == 0) {
+		error = (*filldir)(ent, ".", file->f_pos + 1,
+				   file->f_pos, inode->i_ino,
+				   DT_DIR);
+		if (error < 0)
+			goto out;
+		file->f_pos++;
+	}
+
+	while (1) {
+		error = read_dir(handle, &finddata);
+		if (error)
+			break;
+
+		if (next >= file->f_pos) {
+			size_t len = strlen(finddata.name);
+			ino = iunique(sb, 100);
+			error = (*filldir)(ent, finddata.name, len,
+					   file->f_pos, ino,
+					   (finddata.attrib & _A_SUBDIR) ?
+					    DT_DIR : DT_REG);
+			if (error)
+				break;
+			file->f_pos++;
+		}
+		next++;
+	}
+out:
+	close_dir(handle);
+	return 0;
+}
+
+static int dafs_file_open(struct inode *ino, struct file *file)
+{
+	static DEFINE_MUTEX(open_mutex);
+	char *name;
+	int mode, fmode, flags = 0, r = 0, w = 0, fd;
+	int i;
+
+	fmode = file->f_mode & (FMODE_READ | FMODE_WRITE);
+	if ((fmode & DAFS_I(ino)->mode) == fmode)
+		return 0;
+
+	mode = ino->i_mode & (DA_S_IWUSR | DA_S_IRUSR);
+
+	mode |= DAFS_I(ino)->mode;
+
+	DAFS_I(ino)->mode |= fmode;
+	if (DAFS_I(ino)->mode & FMODE_READ)
+		r = 1;
+	if (DAFS_I(ino)->mode & FMODE_WRITE) {
+		w = 1;
+		r = 1;
+	}
+
+retry:
+	if (r && !w)
+		flags |= DA_O_RDONLY;
+	else if (!r && w)
+		flags |= DA_O_WRONLY;
+	else if (r && w)
+		flags |= DA_O_RDWR;
+
+	if (file->f_flags & O_CREAT)
+		flags |= DA_O_CREAT;
+
+	if (file->f_flags & O_TRUNC)
+		flags |= DA_O_TRUNC;
+
+	/*
+	 * Set the affinity for this file handle to all CPUs. If we
+	 * don't do this then, if the process that opened the file
+	 * migrates to a different cpu, the FileServer will not accept
+	 * the file handle.
+	 */
+	for (i = 0; i < NR_CPUS; i++) {
+		u8 hwthread = cpu_2_hwthread_id[i];
+		flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread));
+	}
+
+	name = dentry_name(file->f_path.dentry);
+	if (name == NULL)
+		return -ENOMEM;
+
+	fd = fscall(DA_OP_OPEN, (int)name, flags, mode, strlen(name), 0);
+	__putname(name);
+	if (fd < 0)
+		return fd;
+
+	mutex_lock(&open_mutex);
+	/* somebody else had handled it first? */
+	if ((mode & DAFS_I(ino)->mode) == mode) {
+		mutex_unlock(&open_mutex);
+		return 0;
+	}
+	if ((mode | DAFS_I(ino)->mode) != mode) {
+		mode |= DAFS_I(ino)->mode;
+		mutex_unlock(&open_mutex);
+		close_file(&fd);
+		goto retry;
+	}
+	DAFS_I(ino)->fd = fd;
+	DAFS_I(ino)->mode = mode;
+	mutex_unlock(&open_mutex);
+
+	return 0;
+}
+
+static const struct file_operations dafs_file_fops = {
+	.llseek		= generic_file_llseek,
+	.read		= do_sync_read,
+	.splice_read	= generic_file_splice_read,
+	.aio_read	= generic_file_aio_read,
+	.aio_write	= generic_file_aio_write,
+	.write		= do_sync_write,
+	.mmap		= generic_file_mmap,
+	.open		= dafs_file_open,
+	.release	= NULL,
+};
+
+static const struct file_operations dafs_dir_fops = {
+	.llseek		= generic_file_llseek,
+	.readdir	= dafs_readdir,
+	.read		= generic_read_dir,
+};
+
+static int read_file(int fd, unsigned long long *offset, const char *buf,
+		     int len)
+{
+	int n;
+
+	n = fscall(DA_OP_PREAD, fd, (int)buf, len, (int)*offset, 0);
+
+	if (n < 0)
+		return -fserrno;
+
+	return n;
+}
+
+static int write_file(int fd, unsigned long long *offset, const char *buf,
+		      int len)
+{
+	int n;
+
+	n = fscall(DA_OP_PWRITE, fd, (int)buf, len, (int)*offset, 0);
+
+	if (n < 0)
+		return -fserrno;
+
+	return n;
+}
+
+static int dafs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode = mapping->host;
+	char *buffer;
+	unsigned long long base;
+	int count = PAGE_CACHE_SIZE;
+	int end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+	int err;
+
+	if (page->index >= end_index)
+		count = inode->i_size & (PAGE_CACHE_SIZE-1);
+
+	buffer = kmap(page);
+	base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;
+
+	err = write_file(DAFS_I(inode)->fd, &base, buffer, count);
+	if (err != count) {
+		ClearPageUptodate(page);
+		goto out;
+	}
+
+	if (base > inode->i_size)
+		inode->i_size = base;
+
+	if (PageError(page))
+		ClearPageError(page);
+	err = 0;
+
+ out:
+	kunmap(page);
+
+	unlock_page(page);
+	return err;
+}
+
+static int dafs_readpage(struct file *file, struct page *page)
+{
+	char *buffer;
+	long long start;
+	int err = 0;
+
+	start = (long long) page->index << PAGE_CACHE_SHIFT;
+	buffer = kmap(page);
+	err = read_file(FILE_DAFS_I(file)->fd, &start, buffer,
+			PAGE_CACHE_SIZE);
+	if (err < 0)
+		goto out;
+
+	memset(&buffer[err], 0, PAGE_CACHE_SIZE - err);
+
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	if (PageError(page))
+		ClearPageError(page);
+	err = 0;
+ out:
+	kunmap(page);
+	unlock_page(page);
+	return err;
+}
+
+int dafs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+
+	*pagep = grab_cache_page_write_begin(mapping, index, flags);
+	if (!*pagep)
+		return -ENOMEM;
+	return 0;
+}
+
+int dafs_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
+{
+	struct inode *inode = mapping->host;
+	void *buffer;
+	unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+	int err;
+
+	buffer = kmap(page);
+	err = write_file(FILE_DAFS_I(file)->fd, &pos, buffer + from, copied);
+	kunmap(page);
+
+	if (!PageUptodate(page) && err == PAGE_CACHE_SIZE)
+		SetPageUptodate(page);
+
+	/*
+	 * If err > 0, write_file has added err to pos, so we are comparing
+	 * i_size against the last byte written.
+	 */
+	if (err > 0 && (pos > inode->i_size))
+		inode->i_size = pos;
+	unlock_page(page);
+	page_cache_release(page);
+
+	return err;
+}
+
+static const struct address_space_operations dafs_aops = {
+	.writepage	= dafs_writepage,
+	.readpage	= dafs_readpage,
+	.set_page_dirty	= __set_page_dirty_nobuffers,
+	.write_begin	= dafs_write_begin,
+	.write_end	= dafs_write_end,
+};
+
+static int read_name(struct inode *ino, char *name)
+{
+	dev_t rdev;
+	struct da_stat st;
+	int err = stat_file(name, &st, -1);
+	if (err)
+		return err;
+
+	/* No valid maj and min from DA.*/
+	rdev = MKDEV(0, 0);
+
+	switch (st.st_mode & S_IFMT) {
+	case S_IFDIR:
+		ino->i_op = &dafs_dir_iops;
+		ino->i_fop = &dafs_dir_fops;
+		break;
+	case S_IFCHR:
+	case S_IFBLK:
+	case S_IFIFO:
+	case S_IFSOCK:
+		init_special_inode(ino, st.st_mode & S_IFMT, rdev);
+		ino->i_op = &dafs_iops;
+		break;
+
+	case S_IFLNK:
+	default:
+		ino->i_op = &dafs_iops;
+		ino->i_fop = &dafs_file_fops;
+		ino->i_mapping->a_ops = &dafs_aops;
+	}
+
+	ino->i_ino = st.st_ino;
+	ino->i_mode = st.st_mode;
+	set_nlink(ino, st.st_nlink);
+
+	ino->i_uid = st.st_uid;
+	ino->i_gid = st.st_gid;
+	ino->i_atime.tv_sec = st.st_atime;
+	ino->i_atime.tv_nsec = 0;
+	ino->i_mtime.tv_sec = st.st_mtime;
+	ino->i_mtime.tv_nsec = 0;
+	ino->i_ctime.tv_sec = st.st_ctime;
+	ino->i_ctime.tv_nsec = 0;
+	ino->i_size = st.st_size;
+	ino->i_blocks = st.st_blocks;
+	return 0;
+}
+
+static int dafs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+		       bool excl)
+{
+	struct inode *inode;
+	char *name;
+	int error, fd;
+	int damode;
+	int creat_flags = DA_O_TRUNC | DA_O_CREAT | DA_O_WRONLY;
+	int i;
+
+	inode = dafs_iget(dir->i_sb);
+	if (IS_ERR(inode)) {
+		error = PTR_ERR(inode);
+		goto out;
+	}
+
+	damode = mode & (DA_S_IWUSR | DA_S_IRUSR);
+
+	error = -ENOMEM;
+	name = dentry_name(dentry);
+	if (name == NULL)
+		goto out_put;
+
+	/*
+	 * creat() will only create text mode files on a Windows host
+	 * at present.  Replicate the creat() functionality with an
+	 * open() call, which always creates binary files. Set the
+	 * affinity to all hardware threads.
+	 */
+	for (i = 0; i < NR_CPUS; i++) {
+		u8 hwthread = cpu_2_hwthread_id[i];
+		creat_flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread));
+	}
+
+	fd = fscall(DA_OP_OPEN, (int)name, creat_flags, damode, strlen(name),
+		    0);
+	if (fd < 0)
+		error = fd;
+	else
+		error = read_name(inode, name);
+
+	kfree(name);
+	if (error)
+		goto out_put;
+
+	DAFS_I(inode)->fd = fd;
+	DAFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
+	d_instantiate(dentry, inode);
+	return 0;
+
+ out_put:
+	iput(inode);
+ out:
+	return error;
+}
+
+static struct dentry *dafs_lookup(struct inode *ino, struct dentry *dentry,
+				  unsigned int flags)
+{
+	struct inode *inode;
+	char *name;
+	int err;
+
+	inode = dafs_iget(ino->i_sb);
+	if (IS_ERR(inode)) {
+		err = PTR_ERR(inode);
+		goto out;
+	}
+
+	err = -ENOMEM;
+	name = dentry_name(dentry);
+	if (name == NULL)
+		goto out_put;
+
+	err = read_name(inode, name);
+
+	__putname(name);
+	if (err == -ENOENT) {
+		iput(inode);
+		inode = NULL;
+	} else if (err)
+		goto out_put;
+
+	d_add(dentry, inode);
+	return NULL;
+
+ out_put:
+	iput(inode);
+ out:
+	return ERR_PTR(err);
+}
+
+int dafs_link(struct dentry *to, struct inode *ino, struct dentry *from)
+{
+	char *from_name, *to_name;
+	int err;
+
+	from_name = dentry_name(from);
+	if (from_name == NULL)
+		return -ENOMEM;
+	to_name = dentry_name(to);
+	if (to_name == NULL) {
+		__putname(from_name);
+		return -ENOMEM;
+	}
+	err = -EINVAL;
+	__putname(from_name);
+	__putname(to_name);
+	return err;
+}
+
+int dafs_unlink(struct inode *ino, struct dentry *dentry)
+{
+	char *file;
+	int err;
+
+	file = dentry_name(dentry);
+	if (file == NULL)
+		return -ENOMEM;
+
+	err = fscall(DA_OP_UNLINK, (int)file, 0, 0, 0, 0);
+	__putname(file);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+static int do_mkdir(const char *file, int mode)
+{
+	int err;
+
+	err = fscall(DA_OP_MKDIR, (int)file, mode, strlen(file), 0, 0);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+int dafs_mkdir(struct inode *ino, struct dentry *dentry, umode_t mode)
+{
+	char *file;
+	int err;
+
+	file = dentry_name(dentry);
+	if (file == NULL)
+		return -ENOMEM;
+	err = do_mkdir(file, mode);
+	__putname(file);
+	return err;
+}
+
+static int do_rmdir(const char *file)
+{
+	int err;
+
+	err = fscall(DA_OP_RMDIR, (int)file, strlen(file), 0, 0, 0);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+int dafs_rmdir(struct inode *ino, struct dentry *dentry)
+{
+	char *file;
+	int err;
+
+	file = dentry_name(dentry);
+	if (file == NULL)
+		return -ENOMEM;
+	err = do_rmdir(file);
+	__putname(file);
+	return err;
+}
+
+int dafs_rename(struct inode *from_ino, struct dentry *from,
+		  struct inode *to_ino, struct dentry *to)
+{
+	char *from_name, *to_name;
+	int err;
+
+	from_name = dentry_name(from);
+	if (from_name == NULL)
+		return -ENOMEM;
+	to_name = dentry_name(to);
+	if (to_name == NULL) {
+		__putname(from_name);
+		return -ENOMEM;
+	}
+	err = -EINVAL;
+	__putname(from_name);
+	__putname(to_name);
+	return err;
+}
+
+static const struct inode_operations dafs_iops = {
+	.create		= dafs_create,
+	.link		= dafs_link,
+	.unlink		= dafs_unlink,
+	.mkdir		= dafs_mkdir,
+	.rmdir		= dafs_rmdir,
+	.rename		= dafs_rename,
+};
+
+static const struct inode_operations dafs_dir_iops = {
+	.create		= dafs_create,
+	.lookup		= dafs_lookup,
+	.link		= dafs_link,
+	.unlink		= dafs_unlink,
+	.mkdir		= dafs_mkdir,
+	.rmdir		= dafs_rmdir,
+	.rename		= dafs_rename,
+};
+
+static char *host_root_path = ".";
+
+static int dafs_fill_sb_common(struct super_block *sb, void *d, int silent)
+{
+	struct inode *root_inode;
+	int err;
+
+	sb->s_blocksize = 1024;
+	sb->s_blocksize_bits = 10;
+	sb->s_magic = DAFS_SUPER_MAGIC;
+	sb->s_op = &dafs_sbops;
+	sb->s_d_op = &dafs_dentry_ops;
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+	err = -ENOMEM;
+
+	root_inode = new_inode(sb);
+	if (!root_inode)
+		goto out;
+
+	err = read_name(root_inode, host_root_path);
+	if (err)
+		goto out_put;
+
+	err = -ENOMEM;
+	sb->s_fs_info = host_root_path;
+	sb->s_root = d_make_root(root_inode);
+	if (sb->s_root == NULL)
+		goto out;
+
+	return 0;
+
+out_put:
+	iput(root_inode);
+out:
+	return err;
+}
+
+static struct dentry *dafs_read_sb(struct file_system_type *type,
+				   int flags, const char *dev_name,
+				   void *data)
+{
+	if (!metag_da_enabled())
+		return ERR_PTR(-ENODEV);
+	return mount_nodev(type, flags, data, dafs_fill_sb_common);
+}
+
+static struct file_system_type dafs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "dafs",
+	.mount		= dafs_read_sb,
+	.kill_sb	= kill_anon_super,
+	.fs_flags	= 0,
+};
+
+static int __init init_dafs(void)
+{
+	return register_filesystem(&dafs_type);
+}
+
+static void __exit exit_dafs(void)
+{
+	unregister_filesystem(&dafs_type);
+}
+
+module_init(init_dafs)
+module_exit(exit_dafs)
+MODULE_LICENSE("GPL");
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (37 preceding siblings ...)
  2012-10-31 16:14   ` James Hogan
@ 2012-10-31 16:26 ` James Hogan
  2012-11-09 15:06   ` Arnd Bergmann
  2012-10-31 22:33 ` Tony Breeds
  2012-11-09 15:16 ` Arnd Bergmann
  40 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 16:26 UTC (permalink / raw)
  To: linux-arch

On 31/10/12 16:13, James Hogan wrote:
> This patchset adds core architecture support to Linux for Imagination's
> Meta ATP (Meta 1) and HTP (Meta 2) processor cores. It is primarily an
> RFC patchset to get some early feedback about what changes we need to
> make, although of course all feedback is most welcome.
> 
> The patches are based on v3.6 for now, and can also be found in the
> following git tree:
>   git://github.com/jahogan/metag-linux.git metag-core
> 
> Meta cores are 32-bit, hardware multithreaded, general purpose, embedded
> processors which also feature a DSP instruction set, and can be found in
> many digital radios. They are capable of running different operating
> systems on different hardware threads, for example a digital radio might
> run RTOSes for DAB decoding and audio decoding on 3 hardware threads,
> and run Linux on the 4th hardware thread to manage the user interface,
> networking etc. HTPs are also capable of running SMP Linux on multiple
> hardware threads.
> 
> Instruction set and architecture overview documents can be found on the
> following page (these currently require registration/NDA, a restriction
> which was not intended and will shortly be removed):
>   http://www.imgtec.com/downloads.asp
> 
> James Hogan (40):
>   asm-generic/io.h: remove asm/cacheflush.h include
>   asm-generic/unistd.h: handle symbol prefixes in cond_syscall
>   Add CONFIG_HAVE_64BIT_ALIGNED_STRUCT for taskstats
>   trace/ring_buffer: handle 64bit aligned structs
>   Revert some of "binfmt_elf: cleanups"
>   of/vendor-prefixes: add Imagination Technologies
>   metag: Add MAINTAINERS entry
>   metag: Boot

Apologies. It appears the following two patches were too large and got
blocked. I'll split them up. In the mean time, here are github links:

>   metag: TBX

https://github.com/jahogan/metag-linux/commit/30d83683e23b88679762457fb0ec71da488a1298

>   metag: Memory management

https://github.com/jahogan/metag-linux/commit/34c0fe0d6ab61f83a56f9e9d3ea04adddaa7a1d4

Cheers
James

>   metag: Signal handling
>   metag: Build infrastructure
>   metag: Device tree
>   metag: Ptrace
>   metag: Time keeping
>   metag: Traps
>   metag: IRQ handling
>   metag: System Calls
>   metag: Scheduling/Process management
>   metag: Module support
>   metag: Atomics, locks and bitops
>   metag: Basic documentation
>   metag: SMP support
>   metag: DMA
>   metag: optimised library functions
>   metag: Stack unwinding
>   metag: various other headers
>   metag: Perf
>   metag: ftrace support
>   scripts/checkstack.pl: Add metag support
>   char: don't build rtc or genrtc on METAG
>   i8042: don't build on METAG
>   parport: don't build on METAG
>   musb: don't redefine {read,write}s{l,w,b} on metag
>   vga console: don't build on METAG
>   metag: OProfile
>   metag: Various sysfs drivers
>   metag: add JTAG Debug Adapter (DA) support
>   tty/metag_da: add metag DA TTY driver
>   fs: dafs: Add DAFS filesystem for metag
> 
>  Documentation/00-INDEX                             |    2 +
>  .../devicetree/bindings/metag/meta-intc.txt        |   92 ++
>  .../devicetree/bindings/vendor-prefixes.txt        |    3 +-
>  Documentation/kernel-parameters.txt                |    4 +
>  Documentation/metag/00-INDEX                       |    2 +
>  MAINTAINERS                                        |   10 +
>  arch/Kconfig                                       |   16 +
>  arch/metag/Kconfig                                 |  325 +++++
>  arch/metag/Kconfig.debug                           |   40 +
>  arch/metag/Kconfig.soc                             |   58 +
>  arch/metag/Makefile                                |  101 ++
>  arch/metag/boot/.gitignore                         |    4 +
>  arch/metag/boot/Makefile                           |   72 +
>  arch/metag/boot/dtb/Makefile                       |   32 +
>  arch/metag/boot/dts/skeleton.dts                   |   10 +
>  arch/metag/boot/dts/skeleton.dtsi                  |   14 +
>  arch/metag/configs/meta1_defconfig                 |   45 +
>  arch/metag/configs/meta2_defconfig                 |   45 +
>  arch/metag/configs/meta2_smp_defconfig             |   46 +
>  arch/metag/include/asm/Kbuild                      |   58 +
>  arch/metag/include/asm/atomic.h                    |   53 +
>  arch/metag/include/asm/atomic_lnkget.h             |  245 ++++
>  arch/metag/include/asm/atomic_lock1.h              |  160 +++
>  arch/metag/include/asm/barrier.h                   |   88 ++
>  arch/metag/include/asm/bitops.h                    |  135 ++
>  arch/metag/include/asm/bug.h                       |   12 +
>  arch/metag/include/asm/byteorder.h                 |    1 +
>  arch/metag/include/asm/cache.h                     |   23 +
>  arch/metag/include/asm/cacheflush.h                |  203 +++
>  arch/metag/include/asm/cachepart.h                 |   42 +
>  arch/metag/include/asm/checksum.h                  |   92 ++
>  arch/metag/include/asm/clock.h                     |   51 +
>  arch/metag/include/asm/cmpxchg.h                   |   65 +
>  arch/metag/include/asm/cmpxchg_irq.h               |   42 +
>  arch/metag/include/asm/cmpxchg_lnkget.h            |   86 ++
>  arch/metag/include/asm/cmpxchg_lock1.h             |   48 +
>  arch/metag/include/asm/core-sysfs.h                |    9 +
>  arch/metag/include/asm/core_reg.h                  |    7 +
>  arch/metag/include/asm/cpu.h                       |   14 +
>  arch/metag/include/asm/da.h                        |   43 +
>  arch/metag/include/asm/delay.h                     |   29 +
>  arch/metag/include/asm/div64.h                     |   12 +
>  arch/metag/include/asm/dma-mapping.h               |  183 +++
>  arch/metag/include/asm/elf.h                       |  131 ++
>  arch/metag/include/asm/fixmap.h                    |   99 ++
>  arch/metag/include/asm/ftrace.h                    |   23 +
>  arch/metag/include/asm/gpio.h                      |    4 +
>  arch/metag/include/asm/highmem.h                   |   62 +
>  arch/metag/include/asm/hugetlb.h                   |   82 ++
>  arch/metag/include/asm/hw_breakpoints.h            |   30 +
>  arch/metag/include/asm/hwthread.h                  |   44 +
>  arch/metag/include/asm/io.h                        |   96 ++
>  arch/metag/include/asm/irq.h                       |   79 +
>  arch/metag/include/asm/irq_external.h              |   33 +
>  arch/metag/include/asm/irq_internal.h              |   16 +
>  arch/metag/include/asm/irqflags.h                  |  100 ++
>  arch/metag/include/asm/l2cache.h                   |  268 ++++
>  arch/metag/include/asm/linkage.h                   |    7 +
>  arch/metag/include/asm/lock.h                      |   17 +
>  arch/metag/include/asm/mach/arch.h                 |   81 ++
>  arch/metag/include/asm/mman.h                      |   14 +
>  arch/metag/include/asm/mmu.h                       |   77 +
>  arch/metag/include/asm/mmu_context.h               |  113 ++
>  arch/metag/include/asm/mmzone.h                    |   49 +
>  arch/metag/include/asm/module.h                    |   39 +
>  arch/metag/include/asm/page.h                      |  132 ++
>  arch/metag/include/asm/perf_event.h                |    4 +
>  arch/metag/include/asm/pgalloc.h                   |   79 +
>  arch/metag/include/asm/pgtable.h                   |  370 +++++
>  arch/metag/include/asm/processor.h                 |  209 +++
>  arch/metag/include/asm/prom.h                      |   23 +
>  arch/metag/include/asm/ptrace.h                    |   50 +
>  arch/metag/include/asm/resource.h                  |    7 +
>  arch/metag/include/asm/setup.h                     |   11 +
>  arch/metag/include/asm/sigcontext.h                |   23 +
>  arch/metag/include/asm/siginfo.h                   |    8 +
>  arch/metag/include/asm/smp.h                       |   31 +
>  arch/metag/include/asm/sparsemem.h                 |   16 +
>  arch/metag/include/asm/spinlock.h                  |   22 +
>  arch/metag/include/asm/spinlock_lnkget.h           |  261 ++++
>  arch/metag/include/asm/spinlock_lock1.h            |  184 +++
>  arch/metag/include/asm/spinlock_types.h            |   20 +
>  arch/metag/include/asm/stacktrace.h                |   20 +
>  arch/metag/include/asm/string.h                    |   13 +
>  arch/metag/include/asm/swab.h                      |   26 +
>  arch/metag/include/asm/switch.h                    |   22 +
>  arch/metag/include/asm/syscall.h                   |  104 ++
>  arch/metag/include/asm/syscalls.h                  |   67 +
>  arch/metag/include/asm/tbx/Kbuild                  |    8 +
>  arch/metag/include/asm/tbx/compasm.inc             |   84 ++
>  arch/metag/include/asm/tbx/machine.inc             |   24 +
>  arch/metag/include/asm/tbx/metac_1_2.inc           |  606 ++++++++
>  arch/metag/include/asm/tbx/metac_2_1.inc           | 1290 +++++++++++++++++
>  arch/metag/include/asm/tbx/metac_all.inc           |   28 +
>  arch/metag/include/asm/tbx/metag.inc               | 1340 ++++++++++++++++++
>  arch/metag/include/asm/tbx/metagdsp.inc            |  506 +++++++
>  arch/metag/include/asm/tbx/metagtbx.h              | 1492 ++++++++++++++++++++
>  arch/metag/include/asm/tcm.h                       |   30 +
>  arch/metag/include/asm/thread_info.h               |  157 ++
>  arch/metag/include/asm/tlb.h                       |   36 +
>  arch/metag/include/asm/tlbflush.h                  |   80 ++
>  arch/metag/include/asm/topology.h                  |   53 +
>  arch/metag/include/asm/traps.h                     |   46 +
>  arch/metag/include/asm/uaccess.h                   |  245 ++++
>  arch/metag/include/asm/unistd.h                    |   28 +
>  arch/metag/include/asm/user_gateway.h              |   44 +
>  arch/metag/kernel/.gitignore                       |    1 +
>  arch/metag/kernel/Makefile                         |   40 +
>  arch/metag/kernel/asm-offsets.c                    |   14 +
>  arch/metag/kernel/cachepart.c                      |  125 ++
>  arch/metag/kernel/clock.c                          |   53 +
>  arch/metag/kernel/core_reg.c                       |  120 ++
>  arch/metag/kernel/cpu/Kconfig                      |   53 +
>  arch/metag/kernel/cpu/Makefile                     |    8 +
>  arch/metag/kernel/cpu/core-sysfs.c                 |   60 +
>  arch/metag/kernel/cpu/counters/Makefile            |    7 +
>  arch/metag/kernel/cpu/counters/amacount.c          |  227 +++
>  arch/metag/kernel/cpu/counters/cyclecount.c        |  135 ++
>  arch/metag/kernel/cpu/counters/perfcount.c         |  154 ++
>  arch/metag/kernel/cpu/l2cache-control.c            |  289 ++++
>  arch/metag/kernel/cpu/memory-arbiter.c             |  133 ++
>  arch/metag/kernel/cpu/write-combiner.c             |  215 +++
>  arch/metag/kernel/da.c                             |   24 +
>  arch/metag/kernel/devtree.c                        |  100 ++
>  arch/metag/kernel/dma.c                            |  521 +++++++
>  arch/metag/kernel/ftrace.c                         |  127 ++
>  arch/metag/kernel/ftrace_stub.S                    |   76 +
>  arch/metag/kernel/head.S                           |   57 +
>  arch/metag/kernel/hw_breakpoints.c                 |  102 ++
>  arch/metag/kernel/irq.c                            |  277 ++++
>  arch/metag/kernel/irq_external.c                   |  886 ++++++++++++
>  arch/metag/kernel/irq_internal.c                   |  235 +++
>  arch/metag/kernel/kick.c                           |   94 ++
>  arch/metag/kernel/machines.c                       |   21 +
>  arch/metag/kernel/metag_ksyms.c                    |   80 ++
>  arch/metag/kernel/module.c                         |  284 ++++
>  arch/metag/kernel/perf_callchain.c                 |   96 ++
>  arch/metag/kernel/process.c                        |  487 +++++++
>  arch/metag/kernel/ptrace.c                         |  503 +++++++
>  arch/metag/kernel/setup.c                          |  585 ++++++++
>  arch/metag/kernel/signal.c                         |  340 +++++
>  arch/metag/kernel/smp.c                            |  582 ++++++++
>  arch/metag/kernel/stacktrace.c                     |  187 +++
>  arch/metag/kernel/sys_metag.c                      |  209 +++
>  arch/metag/kernel/tbiunexp.S                       |   22 +
>  arch/metag/kernel/tcm.c                            |  151 ++
>  arch/metag/kernel/time.c                           |  165 +++
>  arch/metag/kernel/topology.c                       |   77 +
>  arch/metag/kernel/traps.c                          |  967 +++++++++++++
>  arch/metag/kernel/user_gateway.S                   |   99 ++
>  arch/metag/kernel/vmlinux.lds.S                    |   71 +
>  arch/metag/lib/Makefile                            |    7 +
>  arch/metag/lib/ashldi3.S                           |   33 +
>  arch/metag/lib/ashrdi3.S                           |   33 +
>  arch/metag/lib/checksum.c                          |  169 +++
>  arch/metag/lib/clear_page.S                        |   17 +
>  arch/metag/lib/copy_page.S                         |   20 +
>  arch/metag/lib/delay.c                             |   55 +
>  arch/metag/lib/div64.S                             |  108 ++
>  arch/metag/lib/divsi3.S                            |  100 ++
>  arch/metag/lib/ip_fast_csum.S                      |   32 +
>  arch/metag/lib/lshrdi3.S                           |   33 +
>  arch/metag/lib/memcpy.S                            |  185 +++
>  arch/metag/lib/memmove.S                           |  345 +++++
>  arch/metag/lib/memset.S                            |   86 ++
>  arch/metag/lib/modsi3.S                            |   38 +
>  arch/metag/lib/muldi3.S                            |   44 +
>  arch/metag/lib/usercopy.c                          | 1341 ++++++++++++++++++
>  arch/metag/mm/Kconfig                              |  153 ++
>  arch/metag/mm/Makefile                             |   17 +
>  arch/metag/mm/extable.c                            |   15 +
>  arch/metag/mm/fault.c                              |  240 ++++
>  arch/metag/mm/highmem.c                            |  133 ++
>  arch/metag/mm/hugetlbpage.c                        |  291 ++++
>  arch/metag/mm/init.c                               |  448 ++++++
>  arch/metag/mm/ioremap.c                            |   89 ++
>  arch/metag/mm/l2cache.c                            |  222 +++
>  arch/metag/mm/maccess.c                            |   67 +
>  arch/metag/mm/mmu-meta1.c                          |  157 ++
>  arch/metag/mm/mmu-meta2.c                          |  208 +++
>  arch/metag/mm/numa.c                               |  119 ++
>  arch/metag/oprofile/Makefile                       |   11 +
>  arch/metag/oprofile/backtrace.c                    |  134 ++
>  arch/metag/oprofile/backtrace.h                    |    6 +
>  arch/metag/oprofile/op_model_meta12.c              |  244 ++++
>  arch/metag/tbx/Makefile                            |   17 +
>  arch/metag/tbx/tbicache.c                          |  461 ++++++
>  arch/metag/tbx/tbicore.S                           |  137 ++
>  arch/metag/tbx/tbictx.S                            |  368 +++++
>  arch/metag/tbx/tbictxfpu.S                         |  193 +++
>  arch/metag/tbx/tbidefr.S                           |  176 +++
>  arch/metag/tbx/tbidspram.S                         |  165 +++
>  arch/metag/tbx/tbilogf.S                           |   50 +
>  arch/metag/tbx/tbipcx.S                            |  456 ++++++
>  arch/metag/tbx/tbiroot.S                           |   89 ++
>  arch/metag/tbx/tbisoft.S                           |  239 ++++
>  arch/metag/tbx/tbistring.c                         |  148 ++
>  arch/metag/tbx/tbitimer.S                          |  210 +++
>  arch/openrisc/include/asm/io.h                     |    1 +
>  drivers/char/Kconfig                               |    5 +-
>  drivers/input/serio/Kconfig                        |    2 +-
>  drivers/parport/Kconfig                            |    2 +-
>  drivers/tty/Kconfig                                |   13 +
>  drivers/tty/Makefile                               |    1 +
>  drivers/tty/metag_da.c                             |  503 +++++++
>  drivers/usb/musb/musb_io.h                         |    3 +-
>  drivers/video/console/Kconfig                      |    2 +-
>  fs/Kconfig                                         |    1 +
>  fs/Makefile                                        |    1 +
>  fs/binfmt_elf.c                                    |    4 +
>  fs/dafs/Kconfig                                    |    6 +
>  fs/dafs/Makefile                                   |    7 +
>  fs/dafs/dafs.h                                     |   80 ++
>  fs/dafs/inode.c                                    |  830 +++++++++++
>  include/asm-generic/io.h                           |    1 -
>  include/asm-generic/unistd.h                       |    9 +-
>  kernel/taskstats.c                                 |    3 +-
>  kernel/trace/ring_buffer.c                         |    7 +-
>  lib/Kconfig.debug                                  |    6 +-
>  scripts/checkstack.pl                              |    8 +-
>  scripts/recordmcount.pl                            |   12 +
>  221 files changed, 30343 insertions(+), 17 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/metag/meta-intc.txt
>  create mode 100644 Documentation/metag/00-INDEX
>  create mode 100644 arch/metag/Kconfig
>  create mode 100644 arch/metag/Kconfig.debug
>  create mode 100644 arch/metag/Kconfig.soc
>  create mode 100644 arch/metag/Makefile
>  create mode 100644 arch/metag/boot/.gitignore
>  create mode 100644 arch/metag/boot/Makefile
>  create mode 100644 arch/metag/boot/dtb/Makefile
>  create mode 100644 arch/metag/boot/dts/skeleton.dts
>  create mode 100644 arch/metag/boot/dts/skeleton.dtsi
>  create mode 100644 arch/metag/configs/meta1_defconfig
>  create mode 100644 arch/metag/configs/meta2_defconfig
>  create mode 100644 arch/metag/configs/meta2_smp_defconfig
>  create mode 100644 arch/metag/include/asm/Kbuild
>  create mode 100644 arch/metag/include/asm/atomic.h
>  create mode 100644 arch/metag/include/asm/atomic_lnkget.h
>  create mode 100644 arch/metag/include/asm/atomic_lock1.h
>  create mode 100644 arch/metag/include/asm/barrier.h
>  create mode 100644 arch/metag/include/asm/bitops.h
>  create mode 100644 arch/metag/include/asm/bug.h
>  create mode 100644 arch/metag/include/asm/byteorder.h
>  create mode 100644 arch/metag/include/asm/cache.h
>  create mode 100644 arch/metag/include/asm/cacheflush.h
>  create mode 100644 arch/metag/include/asm/cachepart.h
>  create mode 100644 arch/metag/include/asm/checksum.h
>  create mode 100644 arch/metag/include/asm/clock.h
>  create mode 100644 arch/metag/include/asm/cmpxchg.h
>  create mode 100644 arch/metag/include/asm/cmpxchg_irq.h
>  create mode 100644 arch/metag/include/asm/cmpxchg_lnkget.h
>  create mode 100644 arch/metag/include/asm/cmpxchg_lock1.h
>  create mode 100644 arch/metag/include/asm/core-sysfs.h
>  create mode 100644 arch/metag/include/asm/core_reg.h
>  create mode 100644 arch/metag/include/asm/cpu.h
>  create mode 100644 arch/metag/include/asm/da.h
>  create mode 100644 arch/metag/include/asm/delay.h
>  create mode 100644 arch/metag/include/asm/div64.h
>  create mode 100644 arch/metag/include/asm/dma-mapping.h
>  create mode 100644 arch/metag/include/asm/elf.h
>  create mode 100644 arch/metag/include/asm/fixmap.h
>  create mode 100644 arch/metag/include/asm/ftrace.h
>  create mode 100644 arch/metag/include/asm/gpio.h
>  create mode 100644 arch/metag/include/asm/highmem.h
>  create mode 100644 arch/metag/include/asm/hugetlb.h
>  create mode 100644 arch/metag/include/asm/hw_breakpoints.h
>  create mode 100644 arch/metag/include/asm/hwthread.h
>  create mode 100644 arch/metag/include/asm/io.h
>  create mode 100644 arch/metag/include/asm/irq.h
>  create mode 100644 arch/metag/include/asm/irq_external.h
>  create mode 100644 arch/metag/include/asm/irq_internal.h
>  create mode 100644 arch/metag/include/asm/irqflags.h
>  create mode 100644 arch/metag/include/asm/l2cache.h
>  create mode 100644 arch/metag/include/asm/linkage.h
>  create mode 100644 arch/metag/include/asm/lock.h
>  create mode 100644 arch/metag/include/asm/mach/arch.h
>  create mode 100644 arch/metag/include/asm/mman.h
>  create mode 100644 arch/metag/include/asm/mmu.h
>  create mode 100644 arch/metag/include/asm/mmu_context.h
>  create mode 100644 arch/metag/include/asm/mmzone.h
>  create mode 100644 arch/metag/include/asm/module.h
>  create mode 100644 arch/metag/include/asm/page.h
>  create mode 100644 arch/metag/include/asm/perf_event.h
>  create mode 100644 arch/metag/include/asm/pgalloc.h
>  create mode 100644 arch/metag/include/asm/pgtable.h
>  create mode 100644 arch/metag/include/asm/processor.h
>  create mode 100644 arch/metag/include/asm/prom.h
>  create mode 100644 arch/metag/include/asm/ptrace.h
>  create mode 100644 arch/metag/include/asm/resource.h
>  create mode 100644 arch/metag/include/asm/setup.h
>  create mode 100644 arch/metag/include/asm/sigcontext.h
>  create mode 100644 arch/metag/include/asm/siginfo.h
>  create mode 100644 arch/metag/include/asm/smp.h
>  create mode 100644 arch/metag/include/asm/sparsemem.h
>  create mode 100644 arch/metag/include/asm/spinlock.h
>  create mode 100644 arch/metag/include/asm/spinlock_lnkget.h
>  create mode 100644 arch/metag/include/asm/spinlock_lock1.h
>  create mode 100644 arch/metag/include/asm/spinlock_types.h
>  create mode 100644 arch/metag/include/asm/stacktrace.h
>  create mode 100644 arch/metag/include/asm/string.h
>  create mode 100644 arch/metag/include/asm/swab.h
>  create mode 100644 arch/metag/include/asm/switch.h
>  create mode 100644 arch/metag/include/asm/syscall.h
>  create mode 100644 arch/metag/include/asm/syscalls.h
>  create mode 100644 arch/metag/include/asm/tbx/Kbuild
>  create mode 100644 arch/metag/include/asm/tbx/compasm.inc
>  create mode 100644 arch/metag/include/asm/tbx/machine.inc
>  create mode 100644 arch/metag/include/asm/tbx/metac_1_2.inc
>  create mode 100644 arch/metag/include/asm/tbx/metac_2_1.inc
>  create mode 100644 arch/metag/include/asm/tbx/metac_all.inc
>  create mode 100644 arch/metag/include/asm/tbx/metag.inc
>  create mode 100644 arch/metag/include/asm/tbx/metagdsp.inc
>  create mode 100644 arch/metag/include/asm/tbx/metagtbx.h
>  create mode 100644 arch/metag/include/asm/tcm.h
>  create mode 100644 arch/metag/include/asm/thread_info.h
>  create mode 100644 arch/metag/include/asm/tlb.h
>  create mode 100644 arch/metag/include/asm/tlbflush.h
>  create mode 100644 arch/metag/include/asm/topology.h
>  create mode 100644 arch/metag/include/asm/traps.h
>  create mode 100644 arch/metag/include/asm/uaccess.h
>  create mode 100644 arch/metag/include/asm/unistd.h
>  create mode 100644 arch/metag/include/asm/user_gateway.h
>  create mode 100644 arch/metag/kernel/.gitignore
>  create mode 100644 arch/metag/kernel/Makefile
>  create mode 100644 arch/metag/kernel/asm-offsets.c
>  create mode 100644 arch/metag/kernel/cachepart.c
>  create mode 100644 arch/metag/kernel/clock.c
>  create mode 100644 arch/metag/kernel/core_reg.c
>  create mode 100644 arch/metag/kernel/cpu/Kconfig
>  create mode 100644 arch/metag/kernel/cpu/Makefile
>  create mode 100644 arch/metag/kernel/cpu/core-sysfs.c
>  create mode 100644 arch/metag/kernel/cpu/counters/Makefile
>  create mode 100644 arch/metag/kernel/cpu/counters/amacount.c
>  create mode 100644 arch/metag/kernel/cpu/counters/cyclecount.c
>  create mode 100644 arch/metag/kernel/cpu/counters/perfcount.c
>  create mode 100644 arch/metag/kernel/cpu/l2cache-control.c
>  create mode 100644 arch/metag/kernel/cpu/memory-arbiter.c
>  create mode 100644 arch/metag/kernel/cpu/write-combiner.c
>  create mode 100644 arch/metag/kernel/da.c
>  create mode 100644 arch/metag/kernel/devtree.c
>  create mode 100644 arch/metag/kernel/dma.c
>  create mode 100644 arch/metag/kernel/ftrace.c
>  create mode 100644 arch/metag/kernel/ftrace_stub.S
>  create mode 100644 arch/metag/kernel/head.S
>  create mode 100644 arch/metag/kernel/hw_breakpoints.c
>  create mode 100644 arch/metag/kernel/irq.c
>  create mode 100644 arch/metag/kernel/irq_external.c
>  create mode 100644 arch/metag/kernel/irq_internal.c
>  create mode 100644 arch/metag/kernel/kick.c
>  create mode 100644 arch/metag/kernel/machines.c
>  create mode 100644 arch/metag/kernel/metag_ksyms.c
>  create mode 100644 arch/metag/kernel/module.c
>  create mode 100644 arch/metag/kernel/perf_callchain.c
>  create mode 100644 arch/metag/kernel/process.c
>  create mode 100644 arch/metag/kernel/ptrace.c
>  create mode 100644 arch/metag/kernel/setup.c
>  create mode 100644 arch/metag/kernel/signal.c
>  create mode 100644 arch/metag/kernel/smp.c
>  create mode 100644 arch/metag/kernel/stacktrace.c
>  create mode 100644 arch/metag/kernel/sys_metag.c
>  create mode 100644 arch/metag/kernel/tbiunexp.S
>  create mode 100644 arch/metag/kernel/tcm.c
>  create mode 100644 arch/metag/kernel/time.c
>  create mode 100644 arch/metag/kernel/topology.c
>  create mode 100644 arch/metag/kernel/traps.c
>  create mode 100644 arch/metag/kernel/user_gateway.S
>  create mode 100644 arch/metag/kernel/vmlinux.lds.S
>  create mode 100644 arch/metag/lib/Makefile
>  create mode 100644 arch/metag/lib/ashldi3.S
>  create mode 100644 arch/metag/lib/ashrdi3.S
>  create mode 100644 arch/metag/lib/checksum.c
>  create mode 100644 arch/metag/lib/clear_page.S
>  create mode 100644 arch/metag/lib/copy_page.S
>  create mode 100644 arch/metag/lib/delay.c
>  create mode 100644 arch/metag/lib/div64.S
>  create mode 100644 arch/metag/lib/divsi3.S
>  create mode 100644 arch/metag/lib/ip_fast_csum.S
>  create mode 100644 arch/metag/lib/lshrdi3.S
>  create mode 100644 arch/metag/lib/memcpy.S
>  create mode 100644 arch/metag/lib/memmove.S
>  create mode 100644 arch/metag/lib/memset.S
>  create mode 100644 arch/metag/lib/modsi3.S
>  create mode 100644 arch/metag/lib/muldi3.S
>  create mode 100644 arch/metag/lib/usercopy.c
>  create mode 100644 arch/metag/mm/Kconfig
>  create mode 100644 arch/metag/mm/Makefile
>  create mode 100644 arch/metag/mm/extable.c
>  create mode 100644 arch/metag/mm/fault.c
>  create mode 100644 arch/metag/mm/highmem.c
>  create mode 100644 arch/metag/mm/hugetlbpage.c
>  create mode 100644 arch/metag/mm/init.c
>  create mode 100644 arch/metag/mm/ioremap.c
>  create mode 100644 arch/metag/mm/l2cache.c
>  create mode 100644 arch/metag/mm/maccess.c
>  create mode 100644 arch/metag/mm/mmu-meta1.c
>  create mode 100644 arch/metag/mm/mmu-meta2.c
>  create mode 100644 arch/metag/mm/numa.c
>  create mode 100644 arch/metag/oprofile/Makefile
>  create mode 100644 arch/metag/oprofile/backtrace.c
>  create mode 100644 arch/metag/oprofile/backtrace.h
>  create mode 100644 arch/metag/oprofile/op_model_meta12.c
>  create mode 100644 arch/metag/tbx/Makefile
>  create mode 100644 arch/metag/tbx/tbicache.c
>  create mode 100644 arch/metag/tbx/tbicore.S
>  create mode 100644 arch/metag/tbx/tbictx.S
>  create mode 100644 arch/metag/tbx/tbictxfpu.S
>  create mode 100644 arch/metag/tbx/tbidefr.S
>  create mode 100644 arch/metag/tbx/tbidspram.S
>  create mode 100644 arch/metag/tbx/tbilogf.S
>  create mode 100644 arch/metag/tbx/tbipcx.S
>  create mode 100644 arch/metag/tbx/tbiroot.S
>  create mode 100644 arch/metag/tbx/tbisoft.S
>  create mode 100644 arch/metag/tbx/tbistring.c
>  create mode 100644 arch/metag/tbx/tbitimer.S
>  create mode 100644 drivers/tty/metag_da.c
>  create mode 100644 fs/dafs/Kconfig
>  create mode 100644 fs/dafs/Makefile
>  create mode 100644 fs/dafs/dafs.h
>  create mode 100644 fs/dafs/inode.c
> 

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag
  2012-10-31 16:14   ` James Hogan
  (?)
@ 2012-10-31 16:42   ` Al Viro
  2012-10-31 19:39       ` James Hogan
  -1 siblings, 1 reply; 116+ messages in thread
From: Al Viro @ 2012-10-31 16:42 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, linux-fsdevel

On Wed, Oct 31, 2012 at 04:14:21PM +0000, James Hogan wrote:
> +static int fserrno;

... and nothing visible serializing its users.  AFAICS, there's nothing
stopping one thread from doing this fscall thing, then deciding to check
what's in fserrno only to find that *another* thread has also done
fscall - a different one, with different results and different value
stored in fserrno.

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 04/40] trace/ring_buffer: handle 64bit aligned structs
  2012-10-31 16:13 ` [RFC PATCH v1 04/40] trace/ring_buffer: handle 64bit aligned structs James Hogan
@ 2012-10-31 17:59   ` Steven Rostedt
  2012-10-31 18:19     ` James Hogan
  2012-11-01 17:32     ` Will Newton
  0 siblings, 2 replies; 116+ messages in thread
From: Steven Rostedt @ 2012-10-31 17:59 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, Frederic Weisbecker, Ingo Molnar

On Wed, 2012-10-31 at 16:13 +0000, James Hogan wrote:
> Some 32 bit architectures have 64 bit struct alignment (for example
> metag which has 64 bit read/write instructions). These require 8 byte
> alignment of event data too, so use CONFIG_HAVE_64BIT_ALIGNED_STRUCT
> instead of CONFIG_64BIT to decide alignment, and align
> buffer_data_page::data accordingly.

Can you test if trace-cmd still reads this correctly.

git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git

Run a simple:

 trace-cmd record -e all sleep 2
 trace-cmd report

and make sure that the events report correctly your arch (metag). I just
want to make sure that it still reads the binary data correctly. I
should as the /sys/kernel/debug/tracing/events/header_page should
express the difference in the offset of data.

Also, I wish you Cc'd LKML, as there's some changes I would have liked
to look at, but I'm not subscribed to the linux-arch mailing list.

-- Steve

> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Frederic Weisbecker <fweisbec@gmail.com>
> Cc: Ingo Molnar <mingo@redhat.com>
> ---
>  kernel/trace/ring_buffer.c |    7 +++++--
>  1 files changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> index 49491fa..15a63d8 100644
> --- a/kernel/trace/ring_buffer.c
> +++ b/kernel/trace/ring_buffer.c
> @@ -177,7 +177,8 @@ void tracing_off_permanent(void)
>  #define RB_MAX_SMALL_DATA	(RB_ALIGNMENT * RINGBUF_TYPE_DATA_TYPE_LEN_MAX)
>  #define RB_EVNT_MIN_SIZE	8U	/* two 32bit words */
>  
> -#if !defined(CONFIG_64BIT) || defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
> +#if !defined(CONFIG_HAVE_64BIT_ALIGNED_STRUCT) || \
> +	defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
>  # define RB_FORCE_8BYTE_ALIGNMENT	0
>  # define RB_ARCH_ALIGNMENT		RB_ALIGNMENT
>  #else
> @@ -185,6 +186,8 @@ void tracing_off_permanent(void)
>  # define RB_ARCH_ALIGNMENT		8U
>  #endif
>  
> +#define RB_ALIGN_DATA		__aligned(RB_ARCH_ALIGNMENT)
> +
>  /* define RINGBUF_TYPE_DATA for 'case RINGBUF_TYPE_DATA:' */
>  #define RINGBUF_TYPE_DATA 0 ... RINGBUF_TYPE_DATA_TYPE_LEN_MAX
>  
> @@ -333,7 +336,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data);
>  struct buffer_data_page {
>  	u64		 time_stamp;	/* page time stamp */
>  	local_t		 commit;	/* write committed index */
> -	unsigned char	 data[];	/* data of buffer page */
> +	unsigned char	 data[] RB_ALIGN_DATA;	/* data of buffer page */
>  };
>  
>  /*

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 04/40] trace/ring_buffer: handle 64bit aligned structs
  2012-10-31 17:59   ` Steven Rostedt
@ 2012-10-31 18:19     ` James Hogan
  2012-11-01 17:32     ` Will Newton
  1 sibling, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 18:19 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-arch, Frederic Weisbecker, Ingo Molnar

On 31/10/12 17:59, Steven Rostedt wrote:
> On Wed, 2012-10-31 at 16:13 +0000, James Hogan wrote:
>> Some 32 bit architectures have 64 bit struct alignment (for example
>> metag which has 64 bit read/write instructions). These require 8 byte
>> alignment of event data too, so use CONFIG_HAVE_64BIT_ALIGNED_STRUCT
>> instead of CONFIG_64BIT to decide alignment, and align
>> buffer_data_page::data accordingly.
> 
> Can you test if trace-cmd still reads this correctly.
> 
> git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git
> 
> Run a simple:
> 
>  trace-cmd record -e all sleep 2
>  trace-cmd report
> 
> and make sure that the events report correctly your arch (metag). I just
> want to make sure that it still reads the binary data correctly. I
> should as the /sys/kernel/debug/tracing/events/header_page should
> express the difference in the offset of data.

Hi Steven,

Will do, thanks for the suggestion.

> 
> Also, I wish you Cc'd LKML, as there's some changes I would have liked
> to look at, but I'm not subscribed to the linux-arch mailing list.

Sorry. I'll cc LKML for V2.

Thanks
James

> 
> -- Steve
> 
>>
>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
>> Cc: Steven Rostedt <rostedt@goodmis.org>
>> Cc: Frederic Weisbecker <fweisbec@gmail.com>
>> Cc: Ingo Molnar <mingo@redhat.com>
>> ---
>>  kernel/trace/ring_buffer.c |    7 +++++--
>>  1 files changed, 5 insertions(+), 2 deletions(-)
>>
>> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
>> index 49491fa..15a63d8 100644
>> --- a/kernel/trace/ring_buffer.c
>> +++ b/kernel/trace/ring_buffer.c
>> @@ -177,7 +177,8 @@ void tracing_off_permanent(void)
>>  #define RB_MAX_SMALL_DATA	(RB_ALIGNMENT * RINGBUF_TYPE_DATA_TYPE_LEN_MAX)
>>  #define RB_EVNT_MIN_SIZE	8U	/* two 32bit words */
>>  
>> -#if !defined(CONFIG_64BIT) || defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
>> +#if !defined(CONFIG_HAVE_64BIT_ALIGNED_STRUCT) || \
>> +	defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
>>  # define RB_FORCE_8BYTE_ALIGNMENT	0
>>  # define RB_ARCH_ALIGNMENT		RB_ALIGNMENT
>>  #else
>> @@ -185,6 +186,8 @@ void tracing_off_permanent(void)
>>  # define RB_ARCH_ALIGNMENT		8U
>>  #endif
>>  
>> +#define RB_ALIGN_DATA		__aligned(RB_ARCH_ALIGNMENT)
>> +
>>  /* define RINGBUF_TYPE_DATA for 'case RINGBUF_TYPE_DATA:' */
>>  #define RINGBUF_TYPE_DATA 0 ... RINGBUF_TYPE_DATA_TYPE_LEN_MAX
>>  
>> @@ -333,7 +336,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data);
>>  struct buffer_data_page {
>>  	u64		 time_stamp;	/* page time stamp */
>>  	local_t		 commit;	/* write committed index */
>> -	unsigned char	 data[];	/* data of buffer page */
>> +	unsigned char	 data[] RB_ALIGN_DATA;	/* data of buffer page */
>>  };
>>  
>>  /*
> 
> 

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 37/40] metag: Various sysfs drivers
  2012-10-31 16:14 ` [RFC PATCH v1 37/40] metag: Various sysfs drivers James Hogan
@ 2012-10-31 19:30   ` Greg KH
  2012-10-31 19:41     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Greg KH @ 2012-10-31 19:30 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wed, Oct 31, 2012 at 04:14:18PM +0000, James Hogan wrote:
> Add various Meta specific sysfs drivers.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>

As you are creating new sysfs files, please also create new
Documentation/ABI/ files describing what those sysfs files do at the
same time.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-10-31 16:13 ` [RFC PATCH v1 12/40] metag: Build infrastructure James Hogan
@ 2012-10-31 19:35   ` Sam Ravnborg
  2012-10-31 21:34     ` James Hogan
  2012-11-02  9:40     ` James Hogan
  2012-11-09 14:46   ` Arnd Bergmann
  1 sibling, 2 replies; 116+ messages in thread
From: Sam Ravnborg @ 2012-10-31 19:35 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

Hi James.

Some random comments below.
Two general comments.
1) This patch should be the last one - as this enables build of this architecture.
2) this patch should not contain changes to files outside metag
3) there seems to be confusion if the shorthand is meta or metag.
   Please fix it up so metag is used everywhere.

	Sam

> 
> diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
> new file mode 100644
> index 0000000..3a19cb6
> --- /dev/null
> +++ b/arch/metag/Kconfig
> @@ -0,0 +1,292 @@
> +config SYMBOL_PREFIX
> +	string
> +	default "_"
> +
> +config METAG
> +	def_bool y
> +	select EMBEDDED
> +	select HAVE_64BIT_ALIGNED_STRUCT
> +	select HAVE_MEMBLOCK
> +	select HAVE_MEMBLOCK_NODE_MAP
> +	select HAVE_ARCH_TRACEHOOK
> +	select HAVE_IRQ_WORK
> +	select HAVE_KERNEL_GZIP
> +	select HAVE_KERNEL_BZIP2
> +	select HAVE_KERNEL_LZMA
> +	select HAVE_KERNEL_XZ
> +	select HAVE_KERNEL_LZO
> +	select HAVE_SYSCALL_TRACEPOINTS
> +	select HAVE_GENERIC_HARDIRQS
> +	select GENERIC_ATOMIC64
> +	select GENERIC_CLOCKEVENTS
> +	select GENERIC_IRQ_SHOW
> +	select GENERIC_SMP_IDLE_THREAD
> +	select IRQ_DOMAIN
> +	select OF
> +	select OF_EARLY_FLATTREE
Sorting these in alphabetic order avoid merge issues later.

> +	help
> +	  Imagination Technologies Meta processors are a family of multi-
> +	  threaded DSPs designed for embedded systems.
This help entry is not visible in KConfig - just add it as a comment above the config symbol.


> +config IRQ_PER_CPU
> +	def_bool y
This looks obsolete / unused in mainline kernel.

> +config MMU
> +	def_bool y
> +
> +config STACK_GROWSUP
> +	def_bool y
> +
> +config HOTPLUG_CPU
> +	bool "Enable CPU hotplug support"
> +	depends on SMP && HOTPLUG
Today HOTPLUG is always y so the HOTPLUG dependency is not needed.

> +
> +config HIGHMEM
> +	bool "High Memory Support"
> +	depends on MMU

MMU is always y as per above - so the MMU dependeny is not neeeded.

> +	help
> +	  The address space of Meta processors is only 4 Gigabytes large
> +	  and it has to accommodate user address space, kernel address
> +	  space as well as some memory mapped IO. That means that, if you
> +	  have a large amount of physical memory and/or IO, not all of the
> +	  memory can be "permanently mapped" by the kernel. The physical
> +	  memory that is not permanently mapped is called "high memory".
> +
> +	  Depending on the selected kernel/user memory split, minimum
> +	  vmalloc space and actual amount of RAM, you may not need this
> +	  option which should result in a slightly faster kernel.
> +
> +	  If unsure, say n.
> +
> +source "arch/metag/mm/Kconfig"
> +
> +source "arch/metag/Kconfig.soc"
> +
> +config META12
> +	bool
> +	---help---
> +	  Select this from the SoC config symbol to indicate that it contains a
> +	  Meta 1.2 core.
It would be good to have all METAG (or is it META) symbol perfixed with
the same text. Such as METAG_META12 etc.
General comment!



> +
> +config META21
> +	bool
> +	---help---
> +	  Select this from the SoC config symbol to indicate that it contains a
> +	  Meta 2.1 core.
Today we have larely moved away from ---help--- and uses the shorter help
---help--- has 2290 hits
help       has 7841 hits


> +config SMP
> +	bool "Symmetric multi-processing support"
> +	depends on META21
> +	select USE_GENERIC_SMP_HELPERS
> +	---help---
> +	  This enables support for systems with more than one thread running
> +	  Linux. If you have a system with only one thread running Linux,
> +	  say N. Otherwise, say Y.
> +
> +config NR_CPUS
> +	int "Maximum number of CPUs (2-4)" if SMP
> +	range 2 4 if SMP
> +	default "1" if !SMP
> +	default "4" if SMP
> +
> +config SMP_WRITE_REORDERING
> +	bool
> +	---help---
> +	  This attempts to prevent cache-memory incoherence due to external
> +	  reordering of writes from different hardware threads when SMP is
> +	  enabled. It adds fences (system event 0) to smp_mb and smp_rmb in an
> +	  attempt to catch some of the cases, and also before writes to shared
> +	  memory in LOCK1 protected atomics and spinlocks.
> +	  This will not completely prevent cache incoherency on affected cores.
> +
> +config LNKGET_AROUND_CACHE
> +	bool
> +	depends on META21
> +	---help---
> +	  This indicates that the LNKGET/LNKSET instructions go around the
> +	  cache, which requires some extra cache flushes when the memory needs
> +	  to be accessed by normal GET/SET instructions too.
> +
> +choice
> +	prompt "Atomicity primitive"
> +	default ATOMICITY_LNKGET
> +	---help---
> +	  This option selects the mechanism for performing atomic operations.
> +
> +config ATOMICITY_IRQSOFF
> +	depends on !SMP
> +	bool "irqsoff"
> +	---help---
> +	  This option disables interrupts to achieve atomicity. This mechanism
> +	  is not SMP-safe.
> +
> +config ATOMICITY_LNKGET
> +	depends on META21
> +	bool "lnkget/lnkset"
> +	---help---
> +	  This option uses the LNKGET and LNKSET instructions to achieve
> +	  atomicity. LNKGET/LNKSET are load-link/store-conditional instructions.
> +	  Choose this option if your system requires low latency.
> +
> +config ATOMICITY_LOCK1
> +	depends on SMP
> +	bool "lock1"
> +	---help---
> +	  This option uses the LOCK1 instruction for atomicity. This is mainly
> +	  provided as a debugging aid if the lnkget/lnkset atomicity primitive
> +	  isn't working properly.
> +
> +endchoice
> +
> +config META_FPU
> +	bool "FPU Support"
> +	depends on META21
> +	default y
> +	help
> +	  This option allows processes to use FPU hardware available with this
> +	  CPU. If this option is not enabled FPU registers will not be saved
> +	  and restored on context-switch.
> +
> +	  If you plan on running programs which are compiled to use hard floats
> +	  say Y here.
> +
> +config META_DSP
> +	bool "DSP Support"
> +	help
> +	  This option allows processes to use DSP hardware available
> +	  with this CPU. If this option is not enabled DSP registers
> +	  will not be saved and restored on context-switch.
> +
> +	  If you plan on running DSP programs say Y here.
> +
> +config META_PERFCOUNTER_IRQS
> +	bool "PerfCounters interrupt support"
> +	depends on META21
> +	help
> +	  This option enables using interrupts to collect information from
> +	  Performance Counters. This option is supported in new META21
> +	  (starting from HTP265).
> +
> +	  When disabled, Performance Counters information will be collected
> +	  based on Timer Interrupt.
> +
> +menu "Boot options"


Can we use the common config symbols here?
CMDLINE, CMDLINE_OVERWRITE, CMDLINE_BOOL etc.

> +
> +config METAG_CMDLINE_BOOL
> +	bool "Built-in kernel command line"
> +	---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_METAG_CMDLINE.
> +
> +	  Systems with fully functional boot loaders (i.e. non-embedded)
> +	  should leave this option set to 'N'.
> +
> +config METAG_CMDLINE
> +	string "Built-in kernel command string"
> +	depends on METAG_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_METAG_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 METAG_CMDLINE_OVERRIDE
> +	bool "Built-in command line overrides boot loader arguments"
> +	depends on METAG_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
> +
> +menu "Board support"
> +
> +endmenu
No point having an empty menu!?

> --- /dev/null
> +++ b/arch/metag/Makefile
> @@ -0,0 +1,90 @@
> +#
> +# metag/Makefile
> +#
> +# This file is included by the global makefile so that you can add your own
> +# architecture-specific flags and dependencies. Remember to do have actions
> +# for "archclean" cleaning up for this architecture.
> +#
> +# This file is subject to the terms and conditions of the GNU General Public
> +# License.  See the file "COPYING" in the main directory of this archive
> +# for more details.
> +#
> +# Copyright (C) 1994 by Linus Torvalds
> +#               2007,2008,2012 by Imagination Technologies Ltd.
> +#
> +
> +LDFLAGS		:=
> +OBJCOPYFLAGS	:= -O binary -R .note -R .comment -S
> +
> +CHECKFLAGS-$(CONFIG_META12) += -DMETAC_1_2
> +CHECKFLAGS-$(CONFIG_META21) += -DMETAC_2_1

As a rule of thumb UPPERCASE is for exported stuff.
lower-case for local variables.

I would do it like this:
checkflags-$(CONFIG_META12) := -DMETAC_1_2
checkflags-$(CONFIG_META21) := -DMETAC_2_1
CHECKFLAGS   += -D__metag__ $(checkflags-y)

> +CHECKFLAGS	+= -D__metag__ $(CHECKFLAGS-y)
> +
> +KBUILD_DEFCONFIG := meta2_defconfig
> +
> +sflags-$(CONFIG_META12) += -mmetac=1.2
> +
> +ifeq ($(CONFIG_META12),y)
> +# Only use TBI API 1.4 if DSP is enabled for META12 cores
> +sflags-$(CONFIG_META_DSP) += -DTBI_1_4
> +endif
> +
> +sflags-$(CONFIG_META21) += -mmetac=2.1 -DTBI_1_4
> +
> +# Default SoC .c files
> +score-y  :=
> +
> +head-y := arch/metag/kernel/head.o
> +
> +core-y					+= arch/metag/kernel/ \
> +					   arch/metag/mm/

The abuse of \ in makefile in bad practice IMO.
The following is much more readable:

core-y += arch/metag/kernel/
core-y += arch/metag/mm/

Please fix this in all Makefile - if I can convince you.

> +
> +ifneq ($(score-y),)
> +core-y                                  += arch/metag/$(score-y)/
> +endif

How can score-y ever be non-empty?

> +
> +ifneq ($(machdir-y),)
> +core-y  += $(addprefix arch/metag/boards/, \
> +             $(filter-out ., $(patsubst %,%/,$(machdir-y))))
> +endif


Or like this:
machdirs := $(filter-out ., $(patsubst %,%/,$(machdir-y)))
core-y += $(addprefix arch/metag/boards/, $(machdirs))

I see no need to test if machdir-y is non-empty.


> new file mode 100644
> index 0000000..9f6fb26
> --- /dev/null
> +++ b/arch/metag/boot/Makefile
> @@ -0,0 +1,72 @@
> +#
> +# This file is subject to the terms and conditions of the GNU General Public
> +# License.  See the file "COPYING" in the main directory of this archive
> +# for more details.
> +#
> +# Copyright (C) 2007,2012  Imagination Technologies Ltd.
> +#
> +
> +suffix-y := bin
> +suffix-$(CONFIG_KERNEL_GZIP)	:= gz
> +suffix-$(CONFIG_KERNEL_BZIP2)	:= bz2
> +suffix-$(CONFIG_KERNEL_LZMA)	:= lzma
> +suffix-$(CONFIG_KERNEL_XZ)	:= xz
> +suffix-$(CONFIG_KERNEL_LZO)	:= lzo
> +
> +targets := uImage uImage.gz uImage.bz2 uImage.lzma uImage.xz uImage.lzo \
> +	   uImage.bin
> +extra-y += vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
> +	   vmlinux.bin.xz vmlinux.bin.lzo
> +
> +UIMAGE_LOADADDR = $(CONFIG_PAGE_OFFSET)
> +
> +VMLINUX = vmlinux
> +
> +ifeq ($(CONFIG_FUNCTION_TRACER),y)
> +ORIG_CFLAGS := $(KBUILD_CFLAGS)
> +KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
> +endif
> +
> +$(obj)/vmlinux.bin: $(VMLINUX)
> +	$(Q)$(OBJCOPY) -O binary $(strip-flags) $(VMLINUX) $(obj)/vmlinux.bin

Can you use:
OBJCOPYFLAGS_vmlinux.bin := $(strip-flags)
$(obj)/vmlinux.bin: $(VMLINUX)
	$(call if_changed,objcopy)

It will use the definition of OBJCOPYFLAGS from arch/metag/Makefile
where does strip-flags come from?


> +export suffix-y
lower-case is not exported.
And why this export anyway?

> diff --git a/arch/metag/kernel/Makefile b/arch/metag/kernel/Makefile
> new file mode 100644
> index 0000000..0391546
> --- /dev/null
> +++ b/arch/metag/kernel/Makefile
> @@ -0,0 +1,37 @@
> +#
> +# Makefile for the Linux/Meta kernel.
> +#
> +
> +extra-y	+= head.o
> +extra-y	+= vmlinux.lds
> +
> +obj-y	+= cachepart.o
> +obj-y	+= clock.o
> +obj-y	+= core_reg.o
> +obj-y	+= dma.o
> +obj-y	+= hw_breakpoints.o
> +obj-y	+= irq.o
> +obj-y	+= irq_external.o
> +obj-y	+= kick.o
> +obj-y	+= process.o
> +obj-y	+= ptrace.o
> +obj-y	+= setup.o
> +obj-y	+= signal.o
> +obj-y	+= stacktrace.o
> +obj-y	+= sys_metag.o
> +obj-y	+= tbiunexp.o
> +obj-y	+= time.o
> +obj-y	+= topology.o
> +obj-y	+= traps.o
> +obj-y	+= user_gateway.o

No use of "\" - good!

> +/* ld script to make Meta Linux kernel */
> +
> +#include <asm/thread_info.h>
> +#include <asm/page.h>
> +#include <asm/cache.h>
> +
> +#include <asm-generic/vmlinux.lds.h>
> +
> +OUTPUT_FORMAT("elf32-metag", "elf32-metag", "elf32-metag")
> +OUTPUT_ARCH(metag)
> +ENTRY(__start)
> +
> +_jiffies = _jiffies_64;
> +SECTIONS
> +{
> +  . = CONFIG_PAGE_OFFSET;
> +  _text = .;
I doubt this symbol is used anywhere - as meta adds '_'.

> +  __text = .;
> +  __stext = .;
> +  HEAD_TEXT_SECTION
> +  .text : {
> +	TEXT_TEXT
> +	SCHED_TEXT
> +	LOCK_TEXT
> +	KPROBES_TEXT
> +	IRQENTRY_TEXT
> +	*(.text.*)
> +	*(.gnu.warning)
> +	}
> +
> +  __etext = .;			/* End of text section */
> +
> +  __sdata = .;
> +  RO_DATA_SECTION(PAGE_SIZE)
> +  RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
> +  __edata = .;			/* End of data section */
> +
> +  EXCEPTION_TABLE(16)
Do you have a more descriptive constant than 16?
What does 16 imply here?


> +  NOTES
> +
> +  . = ALIGN(PAGE_SIZE);		/* Init code and data */
> +  ___init_begin = .;
> +  INIT_TEXT_SECTION(PAGE_SIZE)
> +  INIT_DATA_SECTION(16)

Same here - what does 16 imply here?

> +
> +  .init.arch.info : {
> +	  ___arch_info_begin = .;
> +	  *(.arch.info.init)
> +	  ___arch_info_end = .;
> +  }
> +
> +  PERCPU_SECTION(L1_CACHE_BYTES)
> +
> +  ___init_end = .;
> +
> +  BSS_SECTION(0, PAGE_SIZE, 0)
> +
> +  __end = .;
> +	
> +  . = ALIGN(PAGE_SIZE);
> +  __heap_start = .;
> +
> +  DWARF_DEBUG
> +
> +  /* When something in the kernel is NOT compiled as a module, the
> +   * module cleanup code and data are put into these segments.  Both
> +   * can then be thrown away, as cleanup code is never called unless
> +   * it's a module.
> +   */
> +  DISCARDS
> +}

> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 2403a63..6a73b08 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -452,7 +452,7 @@ config SLUB_STATS
>  config DEBUG_KMEMLEAK
>  	bool "Kernel memory leak detector"
>  	depends on DEBUG_KERNEL && EXPERIMENTAL && \
> -		(X86 || ARM || PPC || MIPS || S390 || SPARC64 || SUPERH || MICROBLAZE || TILE)
> +		(X86 || ARM || PPC || MIPS || S390 || SPARC64 || SUPERH || MICROBLAZE || TILE || METAG)
>  
>  	select DEBUG_FS
>  	select STACKTRACE if STACKTRACE_SUPPORT

This is outside arch/ so it belongs in another patch.

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag
  2012-10-31 16:42   ` Al Viro
@ 2012-10-31 19:39       ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 19:39 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-arch, linux-fsdevel

On 31/10/12 16:42, Al Viro wrote:
> On Wed, Oct 31, 2012 at 04:14:21PM +0000, James Hogan wrote:
>> +static int fserrno;
> 
> ... and nothing visible serializing its users.  AFAICS, there's nothing
> stopping one thread from doing this fscall thing, then deciding to check
> what's in fserrno only to find that *another* thread has also done
> fscall - a different one, with different results and different value
> stored in fserrno.

Yep, I'll fix that.

Thanks for taking a look

James


^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag
@ 2012-10-31 19:39       ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 19:39 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-arch, linux-fsdevel

On 31/10/12 16:42, Al Viro wrote:
> On Wed, Oct 31, 2012 at 04:14:21PM +0000, James Hogan wrote:
>> +static int fserrno;
> 
> ... and nothing visible serializing its users.  AFAICS, there's nothing
> stopping one thread from doing this fscall thing, then deciding to check
> what's in fserrno only to find that *another* thread has also done
> fscall - a different one, with different results and different value
> stored in fserrno.

Yep, I'll fix that.

Thanks for taking a look

James


^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 37/40] metag: Various sysfs drivers
  2012-10-31 19:30   ` Greg KH
@ 2012-10-31 19:41     ` James Hogan
  2012-11-09 14:32       ` Arnd Bergmann
  0 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-10-31 19:41 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-arch

On 31/10/12 19:30, Greg KH wrote:
> On Wed, Oct 31, 2012 at 04:14:18PM +0000, James Hogan wrote:
>> Add various Meta specific sysfs drivers.
>>
>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> 
> As you are creating new sysfs files, please also create new
> Documentation/ABI/ files describing what those sysfs files do at the
> same time.

Good point, I'll make sure Documentation is added.

Thanks,
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag
  2012-10-31 16:14   ` James Hogan
  (?)
  (?)
@ 2012-10-31 19:55   ` Myklebust, Trond
  2012-10-31 21:39     ` James Hogan
  2012-11-02  9:33     ` James Hogan
  -1 siblings, 2 replies; 116+ messages in thread
From: Myklebust, Trond @ 2012-10-31 19:55 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, Alexander Viro, linux-fsdevel

On Wed, 2012-10-31 at 16:14 +0000, James Hogan wrote:
> Add the Metag Debug Adapter File System (DAFS), which uses Meta SWITCH
> operations to communicate with a file server on a host computer via a
> JTAG debug adapter.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
> Cc: linux-fsdevel@vger.kernel.org

You might want to note that DAFS as a name for a remote filesystem is
already taken:

  http://en.wikipedia.org/wiki/Direct_Access_File_System

-- 
Trond Myklebust
Linux NFS client maintainer

NetApp
Trond.Myklebust@netapp.com
www.netapp.com

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-10-31 19:35   ` Sam Ravnborg
@ 2012-10-31 21:34     ` James Hogan
  2012-11-02  9:40     ` James Hogan
  1 sibling, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 21:34 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: linux-arch

Hi Sam,

Thanks for taking a look.

On 31/10/12 19:35, Sam Ravnborg wrote:
> 1) This patch should be the last one - as this enables build of this architecture.

Okay, makes sense

> 2) this patch should not contain changes to files outside metag

Okay, I'll split those changes into a separate patch

> 3) there seems to be confusion if the shorthand is meta or metag.
>    Please fix it up so metag is used everywhere.
> 
> 	Sam
> 
>> +config METAG
>> +	def_bool y
>> +	select EMBEDDED
>> +	select HAVE_64BIT_ALIGNED_STRUCT
>> +	select HAVE_MEMBLOCK
>> +	select HAVE_MEMBLOCK_NODE_MAP
>> +	select HAVE_ARCH_TRACEHOOK
>> +	select HAVE_IRQ_WORK
>> +	select HAVE_KERNEL_GZIP
>> +	select HAVE_KERNEL_BZIP2
>> +	select HAVE_KERNEL_LZMA
>> +	select HAVE_KERNEL_XZ
>> +	select HAVE_KERNEL_LZO
>> +	select HAVE_SYSCALL_TRACEPOINTS
>> +	select HAVE_GENERIC_HARDIRQS
>> +	select GENERIC_ATOMIC64
>> +	select GENERIC_CLOCKEVENTS
>> +	select GENERIC_IRQ_SHOW
>> +	select GENERIC_SMP_IDLE_THREAD
>> +	select IRQ_DOMAIN
>> +	select OF
>> +	select OF_EARLY_FLATTREE
> Sorting these in alphabetic order avoid merge issues later.

Good idea

> 
>> +	help
>> +	  Imagination Technologies Meta processors are a family of multi-
>> +	  threaded DSPs designed for embedded systems.
> This help entry is not visible in KConfig - just add it as a comment above the config symbol.

Okay

> 
>> +config IRQ_PER_CPU
>> +	def_bool y
> This looks obsolete / unused in mainline kernel.

True. It appears to have been removed by
6a58fb3bad099076f36f0f30f44507bc3275cdb6 but not from any of the
architectures that selected it. I'll add that to the list.

>> +config HOTPLUG_CPU
>> +	bool "Enable CPU hotplug support"
>> +	depends on SMP && HOTPLUG
> Today HOTPLUG is always y so the HOTPLUG dependency is not needed.

Okay

>> +config HIGHMEM
>> +	bool "High Memory Support"
>> +	depends on MMU
> 
> MMU is always y as per above - so the MMU dependeny is not neeeded.

Okay

>> +config META12
>> +	bool
>> +	---help---
>> +	  Select this from the SoC config symbol to indicate that it contains a
>> +	  Meta 1.2 core.
> It would be good to have all METAG (or is it META) symbol perfixed with
> the same text. Such as METAG_META12 etc.
> General comment!

Yep, makes sense.

>> +config META21
>> +	bool
>> +	---help---
>> +	  Select this from the SoC config symbol to indicate that it contains a
>> +	  Meta 2.1 core.
> Today we have larely moved away from ---help--- and uses the shorter help
> ---help--- has 2290 hits
> help       has 7841 hits

Okay, I think I remember doing the same grep and not really being sure
which to unify towards.

>> +menu "Boot options"
> 
> 
> Can we use the common config symbols here?
> CMDLINE, CMDLINE_OVERWRITE, CMDLINE_BOOL etc.

This was intentional, as they're used in drivers/of/fdt.c, which
interferes a bit with allowing appending of a builtin command line. I'm
happy to make it more uniform with other arches though.

>> +menu "Board support"
>> +
>> +endmenu
> No point having an empty menu!?

Oops, I'll remove until we submit something to put in it.

>> +LDFLAGS		:=
>> +OBJCOPYFLAGS	:= -O binary -R .note -R .comment -S
>> +
>> +CHECKFLAGS-$(CONFIG_META12) += -DMETAC_1_2
>> +CHECKFLAGS-$(CONFIG_META21) += -DMETAC_2_1
> 
> As a rule of thumb UPPERCASE is for exported stuff.
> lower-case for local variables.
> 
> I would do it like this:
> checkflags-$(CONFIG_META12) := -DMETAC_1_2
> checkflags-$(CONFIG_META21) := -DMETAC_2_1
> CHECKFLAGS   += -D__metag__ $(checkflags-y)

Okay, thanks. I'll give all our stuff a scan over to check for this kind
of thing.

>> +core-y					+= arch/metag/kernel/ \
>> +					   arch/metag/mm/
> 
> The abuse of \ in makefile in bad practice IMO.
> The following is much more readable:
> 
> core-y += arch/metag/kernel/
> core-y += arch/metag/mm/
> 
> Please fix this in all Makefile - if I can convince you.

Agreed (it definitely makes merges etc easier)

> 
>> +
>> +ifneq ($(score-y),)
>> +core-y                                  += arch/metag/$(score-y)/
>> +endif
> 
> How can score-y ever be non-empty?

Individual SoCs would set that. I'll remove it until SoC support is
submitted.

> 
>> +
>> +ifneq ($(machdir-y),)
>> +core-y  += $(addprefix arch/metag/boards/, \
>> +             $(filter-out ., $(patsubst %,%/,$(machdir-y))))
>> +endif
> 
> 
> Or like this:
> machdirs := $(filter-out ., $(patsubst %,%/,$(machdir-y)))
> core-y += $(addprefix arch/metag/boards/, $(machdirs))
> 
> I see no need to test if machdir-y is non-empty.

Agreed.


>> +$(obj)/vmlinux.bin: $(VMLINUX)
>> +	$(Q)$(OBJCOPY) -O binary $(strip-flags) $(VMLINUX) $(obj)/vmlinux.bin
> 
> Can you use:
> OBJCOPYFLAGS_vmlinux.bin := $(strip-flags)
> $(obj)/vmlinux.bin: $(VMLINUX)
> 	$(call if_changed,objcopy)
> 
> It will use the definition of OBJCOPYFLAGS from arch/metag/Makefile

Neat, thanks

> where does strip-flags come from?

Nowhere apparently. I suspect it was copied from mips. I'll remove it.

>> +export suffix-y
> lower-case is not exported.
> And why this export anyway?

I think this was derived from sh, but that seems to use it in
arch/sh/boot/compressed/Makefile which we don't have, so I'll remove the
export.

>> diff --git a/arch/metag/kernel/Makefile b/arch/metag/kernel/Makefile
>> new file mode 100644
>> index 0000000..0391546
>> --- /dev/null
>> +++ b/arch/metag/kernel/Makefile
>> @@ -0,0 +1,37 @@

> No use of "\" - good!

:-) the "\"'s were bugging me here when splitting up the patches

>> +/* ld script to make Meta Linux kernel */

>> +  _text = .;
> I doubt this symbol is used anywhere - as meta adds '_'.

It appears to be used by .tmp_kallsyms1.o

>> +  __text = .;
>> +  __stext = .;
>> +  HEAD_TEXT_SECTION
>> +  .text : {
>> +	TEXT_TEXT
>> +	SCHED_TEXT
>> +	LOCK_TEXT
>> +	KPROBES_TEXT
>> +	IRQENTRY_TEXT
>> +	*(.text.*)
>> +	*(.gnu.warning)
>> +	}
>> +
>> +  __etext = .;			/* End of text section */
>> +
>> +  __sdata = .;
>> +  RO_DATA_SECTION(PAGE_SIZE)
>> +  RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
>> +  __edata = .;			/* End of data section */
>> +
>> +  EXCEPTION_TABLE(16)
> Do you have a more descriptive constant than 16?
> What does 16 imply here?

A bunch of other architectures also set this alignment. A couple though
use a cache line size definition, suggesting it's for cache efficiency.
If so we should probably set it to 64 (metag's cache line size).

> 
> 
>> +  NOTES
>> +
>> +  . = ALIGN(PAGE_SIZE);		/* Init code and data */
>> +  ___init_begin = .;
>> +  INIT_TEXT_SECTION(PAGE_SIZE)
>> +  INIT_DATA_SECTION(16)
> 
> Same here - what does 16 imply here?

Almost every other architecture also sets this alignment to 16 (except
frv=8, hexagon=pagesize).

Thanks for all the comments,

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag
  2012-10-31 19:55   ` Myklebust, Trond
@ 2012-10-31 21:39     ` James Hogan
  2012-11-02  9:33     ` James Hogan
  1 sibling, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-10-31 21:39 UTC (permalink / raw)
  To: Myklebust, Trond; +Cc: linux-arch, Alexander Viro, linux-fsdevel

Hi,

On 31/10/12 19:55, Myklebust, Trond wrote:
> On Wed, 2012-10-31 at 16:14 +0000, James Hogan wrote:
>> Add the Metag Debug Adapter File System (DAFS), which uses Meta SWITCH
>> operations to communicate with a file server on a host computer via a
>> JTAG debug adapter.
>>
>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
>> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
>> Cc: linux-fsdevel@vger.kernel.org
> 
> You might want to note that DAFS as a name for a remote filesystem is
> already taken:
> 
>   http://en.wikipedia.org/wiki/Direct_Access_File_System
> 

Hmm, thanks for pointing that out :-)

Cheers
James


^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (38 preceding siblings ...)
  2012-10-31 16:26 ` [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
@ 2012-10-31 22:33 ` Tony Breeds
  2012-11-01 11:42   ` James Hogan
  2012-11-09 15:16 ` Arnd Bergmann
  40 siblings, 1 reply; 116+ messages in thread
From: Tony Breeds @ 2012-10-31 22:33 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

[-- Attachment #1: Type: text/plain, Size: 411 bytes --]

On Wed, Oct 31, 2012 at 04:13:41PM +0000, James Hogan wrote:
> This patchset adds core architecture support to Linux for Imagination's
> Meta ATP (Meta 1) and HTP (Meta 2) processor cores. It is primarily an
> RFC patchset to get some early feedback about what changes we need to
> make, although of course all feedback is most welcome.

Any chance there are patches out there to build a toolchain?

Yours Tony

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 06/40] of/vendor-prefixes: add Imagination Technologies
  2012-10-31 16:13 ` [RFC PATCH v1 06/40] of/vendor-prefixes: add Imagination Technologies James Hogan
@ 2012-11-01  1:38   ` Rob Herring
  2012-11-01  9:20     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Rob Herring @ 2012-11-01  1:38 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, Grant Likely, Rob Landley, devicetree-discuss

On 10/31/2012 11:13 AM, James Hogan wrote:
> The "powervr" prefix which is currently described as "Imagination
> Technologies" isn't really appropriate for non-PowerVR hardware, so
> change the description of "powervr" to "PowerVR (Imagination
> Technologies Ltd.)", and add a separate "img" prefix for "Imagination
> Technologies Ltd.".
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> Cc: devicetree-discuss@lists.ozlabs.org
> ---
>  .../devicetree/bindings/vendor-prefixes.txt        |    3 ++-
>  1 files changed, 2 insertions(+), 1 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index db4d3af..5a94721b 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -24,6 +24,7 @@ gef	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
>  hp	Hewlett Packard
>  ibm	International Business Machines (IBM)
>  idt	Integrated Device Technologies, Inc.
> +img	Imagination Technologies Ltd.
>  intercontrol	Inter Control Group
>  linux	Linux-specific binding
>  marvell	Marvell Technology Group Ltd.
> @@ -34,7 +35,7 @@ nintendo	Nintendo
>  nvidia	NVIDIA
>  nxp	NXP Semiconductors
>  picochip	Picochip Ltd
> -powervr	Imagination Technologies
> +powervr	PowerVR (Imagination Technologies Ltd.)

Perhaps you should add "deprecated, use img"

Otherwise, looks fine and I assume you are going to merge with the rest
of the series:

Acked-by: Rob Herring <rob.herring@calxeda.com>

Rob

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 06/40] of/vendor-prefixes: add Imagination Technologies
  2012-11-01  1:38   ` Rob Herring
@ 2012-11-01  9:20     ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-01  9:20 UTC (permalink / raw)
  To: Rob Herring; +Cc: linux-arch, Grant Likely, Rob Landley, devicetree-discuss

On 01/11/12 01:38, Rob Herring wrote:
> On 10/31/2012 11:13 AM, James Hogan wrote:
>> @@ -34,7 +35,7 @@ nintendo	Nintendo
>>  nvidia	NVIDIA
>>  nxp	NXP Semiconductors
>>  picochip	Picochip Ltd
>> -powervr	Imagination Technologies
>> +powervr	PowerVR (Imagination Technologies Ltd.)
> 
> Perhaps you should add "deprecated, use img"

Yes, that sounds reasonable, I'll change to something like this:
powervr		PowerVR (deprecated, use img)

> Otherwise, looks fine and I assume you are going to merge with the rest
> of the series:

Yeh, I'm happy either way.

> Acked-by: Rob Herring <rob.herring@calxeda.com>

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-10-31 22:33 ` Tony Breeds
@ 2012-11-01 11:42   ` James Hogan
  2012-11-01 23:28     ` Tony Breeds
  2012-11-09 16:52     ` James Hogan
  0 siblings, 2 replies; 116+ messages in thread
From: James Hogan @ 2012-11-01 11:42 UTC (permalink / raw)
  To: Tony Breeds; +Cc: linux-arch

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 31/10/12 22:33, Tony Breeds wrote:
> On Wed, Oct 31, 2012 at 04:13:41PM +0000, James Hogan wrote:
>> This patchset adds core architecture support to Linux for
>> Imagination's Meta ATP (Meta 1) and HTP (Meta 2) processor cores.
>> It is primarily an RFC patchset to get some early feedback about
>> what changes we need to make, although of course all feedback is
>> most welcome.
> 
> Any chance there are patches out there to build a toolchain?

Hi Tony,

We're working on a buildroot release with toolchain patches etc
included, which we hope to make available tomorrow, but which may slip
to late next week (ELC-E).

In the mean time, there is an older buildroot available here (which
requires registration):
http://www.imgtec.com/minimorph

It still uses our legacy system call numbers, so isn't useful for
running any userland code, but it's sufficient to build the kernel.

Download the Minimorph v1.7 release in the MetaFlow SDK. Extract
metag-linux-GPL-Minimorph_1_0-rel007.tar.gz, and in the buildroot
directory, change the config to save a little time:
$ make comet_defconfig

then switch busybox to v1.19.x (default version doesn't build out of
the box with newer versions of make):
$ make menuconfig
	package selection for target
		BusyBox Version

After building buildroot, the CROSS_COMPILE prefix is:
$buildroot/output/host/usr/bin/metag-linux-

Thanks
James
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iQIcBAEBAgAGBQJQkmAUAAoJEKHZs+irPybfj5UQAIPduw5gFld9Cw133l0RWJAp
VPlF+CKN5CGCxQ4jDHAckoI7Yn4Z4Bx/m6luN9u5fKBGeEcS2cXmji81UuSv9oFh
M5WHymfMJEXqGggQX+Ue9ccy3v4X7z95AuwsaxxCT0xhgVmNhWUKNNAT+o2I5YLx
v0Mbs3l2esip97iH0nTZAbsYaC5dZUsCI65bVBZ9vrTRiuMqv8tRo3KpupX2yUcX
BcDBw23x1bligbYtSZyNVMM4WyK+5xDvRomsNlZIePjMUADtx5mwzSeQuxyAJown
gu0mj6lwbyDI+hDudz6kUC5Y+5XC2jEyOBC+xBXiL4fkoVI40WcTO/XDr/3xf8g9
cWYlR+bIPcD98P0MwxHvXZBgia3dpTKC4hVNeW2Pg4veejqQmGgB0r6s/MWLYwB2
3czuYGtz7FufJf0H0lZbhE11anQzsogeHXfAhaJo/6veV53p5ks91Gr+l45VzjXp
ebYoE6BO3afPH4DjvvasnbOvVSgCVdz5g9G+TgGsQ3c+AQ+989+Rq88DFme1SqE4
fRW+0W2zQdSii0DPMYliA3jA0QsRpRkrmGS2BjYWshc4l9TeP+i7t56dVPyPsFFd
TH4cQYDI43popZVZ/EaanEts/EEX04/R/RfzlVafuaBZ/3+ENznQGHQR4L1jaTvq
J5x0MO7Lyj+Rzxh9Us19
=mIEP
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 05/40] Revert some of "binfmt_elf: cleanups"
  2012-10-31 16:13   ` James Hogan
@ 2012-11-01 13:25     ` Mikael Pettersson
  -1 siblings, 0 replies; 116+ messages in thread
From: Mikael Pettersson @ 2012-11-01 13:25 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, Mikael Pettersson, Alexander Viro, linux-fsdevel

James Hogan writes:
 > The commit "binfmt_elf: cleanups"
 > (f670d0ecda73b7438eec9ed108680bc5f5362ad8) removed an ifndef elf_map but
 > this breaks compilation on metag architecture which does define elf_map.
 > 
 > This adds the ifndef back in as it was before, but does not affect the
 > other cleanups made by that patch.

Since this is part of your patch submission to upstream the metag
kernel support, I have no objection to reverting this fragment of
my patch if/when the metag code gets upstreamed.

Acked-by: Mikael Pettersson <mikpe@it.uu.se>

 > 
 > Signed-off-by: James Hogan <james.hogan@imgtec.com>
 > Cc: Mikael Pettersson <mikpe@it.uu.se>
 > Cc: Alexander Viro <viro@zeniv.linux.org.uk>
 > Cc: linux-fsdevel@vger.kernel.org
 > ---
 >  fs/binfmt_elf.c |    4 ++++
 >  1 files changed, 4 insertions(+), 0 deletions(-)
 > 
 > diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
 > index 1b52956..14d1fbd 100644
 > --- a/fs/binfmt_elf.c
 > +++ b/fs/binfmt_elf.c
 > @@ -314,6 +314,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 >  	return 0;
 >  }
 >  
 > +#ifndef elf_map
 > +
 >  static unsigned long elf_map(struct file *filep, unsigned long addr,
 >  		struct elf_phdr *eppnt, int prot, int type,
 >  		unsigned long total_size)
 > @@ -348,6 +350,8 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
 >  	return(map_addr);
 >  }
 >  
 > +#endif /* !elf_map */
 > +
 >  static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
 >  {
 >  	int i, first_idx = -1, last_idx = -1;
 > -- 
 > 1.7.7.6
 > 
 > 

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 05/40] Revert some of "binfmt_elf: cleanups"
@ 2012-11-01 13:25     ` Mikael Pettersson
  0 siblings, 0 replies; 116+ messages in thread
From: Mikael Pettersson @ 2012-11-01 13:25 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, Mikael Pettersson, Alexander Viro, linux-fsdevel

James Hogan writes:
 > The commit "binfmt_elf: cleanups"
 > (f670d0ecda73b7438eec9ed108680bc5f5362ad8) removed an ifndef elf_map but
 > this breaks compilation on metag architecture which does define elf_map.
 > 
 > This adds the ifndef back in as it was before, but does not affect the
 > other cleanups made by that patch.

Since this is part of your patch submission to upstream the metag
kernel support, I have no objection to reverting this fragment of
my patch if/when the metag code gets upstreamed.

Acked-by: Mikael Pettersson <mikpe@it.uu.se>

 > 
 > Signed-off-by: James Hogan <james.hogan@imgtec.com>
 > Cc: Mikael Pettersson <mikpe@it.uu.se>
 > Cc: Alexander Viro <viro@zeniv.linux.org.uk>
 > Cc: linux-fsdevel@vger.kernel.org
 > ---
 >  fs/binfmt_elf.c |    4 ++++
 >  1 files changed, 4 insertions(+), 0 deletions(-)
 > 
 > diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
 > index 1b52956..14d1fbd 100644
 > --- a/fs/binfmt_elf.c
 > +++ b/fs/binfmt_elf.c
 > @@ -314,6 +314,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 >  	return 0;
 >  }
 >  
 > +#ifndef elf_map
 > +
 >  static unsigned long elf_map(struct file *filep, unsigned long addr,
 >  		struct elf_phdr *eppnt, int prot, int type,
 >  		unsigned long total_size)
 > @@ -348,6 +350,8 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
 >  	return(map_addr);
 >  }
 >  
 > +#endif /* !elf_map */
 > +
 >  static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
 >  {
 >  	int i, first_idx = -1, last_idx = -1;
 > -- 
 > 1.7.7.6
 > 
 > 

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 05/40] Revert some of "binfmt_elf: cleanups"
  2012-11-01 13:25     ` Mikael Pettersson
@ 2012-11-01 13:52       ` James Hogan
  -1 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-01 13:52 UTC (permalink / raw)
  To: Mikael Pettersson; +Cc: linux-arch, Alexander Viro, linux-fsdevel

On 01/11/12 13:25, Mikael Pettersson wrote:
> James Hogan writes:
>  > The commit "binfmt_elf: cleanups"
>  > (f670d0ecda73b7438eec9ed108680bc5f5362ad8) removed an ifndef elf_map but
>  > this breaks compilation on metag architecture which does define elf_map.
>  > 
>  > This adds the ifndef back in as it was before, but does not affect the
>  > other cleanups made by that patch.
> 
> Since this is part of your patch submission to upstream the metag
> kernel support, I have no objection to reverting this fragment of
> my patch if/when the metag code gets upstreamed.
> 
> Acked-by: Mikael Pettersson <mikpe@it.uu.se>

Thanks Mikael, I'll keep it as part of the metag patch series so that
they go in together.

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 05/40] Revert some of "binfmt_elf: cleanups"
@ 2012-11-01 13:52       ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-01 13:52 UTC (permalink / raw)
  To: Mikael Pettersson; +Cc: linux-arch, Alexander Viro, linux-fsdevel

On 01/11/12 13:25, Mikael Pettersson wrote:
> James Hogan writes:
>  > The commit "binfmt_elf: cleanups"
>  > (f670d0ecda73b7438eec9ed108680bc5f5362ad8) removed an ifndef elf_map but
>  > this breaks compilation on metag architecture which does define elf_map.
>  > 
>  > This adds the ifndef back in as it was before, but does not affect the
>  > other cleanups made by that patch.
> 
> Since this is part of your patch submission to upstream the metag
> kernel support, I have no objection to reverting this fragment of
> my patch if/when the metag code gets upstreamed.
> 
> Acked-by: Mikael Pettersson <mikpe@it.uu.se>

Thanks Mikael, I'll keep it as part of the metag patch series so that
they go in together.

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 31/40] char: don't build rtc or genrtc on METAG
  2012-10-31 16:14 ` [RFC PATCH v1 31/40] char: don't build rtc or genrtc on METAG James Hogan
@ 2012-11-01 15:39   ` Greg KH
  2012-11-01 17:33     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Greg KH @ 2012-11-01 15:39 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wed, Oct 31, 2012 at 04:14:12PM +0000, James Hogan wrote:
> The metag architecture doesn't have a PC RTC or implement the RTC
> interface that genrtc expects, so add METAG to the list of architectures
> not to build RTC or GEN_RTC on.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> ---
>  drivers/char/Kconfig |    5 +++--
>  1 files changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> index ea6f632..09b64a8 100644
> --- a/drivers/char/Kconfig
> +++ b/drivers/char/Kconfig
> @@ -283,7 +283,8 @@ if RTC_LIB=n
>  config RTC
>  	tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)"
>  	depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV \
> -			&& !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN && !UML
> +			&& !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN \
> +			&& !UML && !METAG

This is getting huge, would it be easier to reverse it and just list the
arches that it does work for?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 04/40] trace/ring_buffer: handle 64bit aligned structs
  2012-10-31 17:59   ` Steven Rostedt
  2012-10-31 18:19     ` James Hogan
@ 2012-11-01 17:32     ` Will Newton
  2012-11-01 19:30       ` Steven Rostedt
  1 sibling, 1 reply; 116+ messages in thread
From: Will Newton @ 2012-11-01 17:32 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: James Hogan, linux-arch, Frederic Weisbecker, Ingo Molnar

On Wed, Oct 31, 2012 at 5:59 PM, Steven Rostedt <rostedt@goodmis.org> wrote:
> On Wed, 2012-10-31 at 16:13 +0000, James Hogan wrote:
>> Some 32 bit architectures have 64 bit struct alignment (for example
>> metag which has 64 bit read/write instructions). These require 8 byte
>> alignment of event data too, so use CONFIG_HAVE_64BIT_ALIGNED_STRUCT
>> instead of CONFIG_64BIT to decide alignment, and align
>> buffer_data_page::data accordingly.
>
> Can you test if trace-cmd still reads this correctly.
>
> git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git
>
> Run a simple:
>
>  trace-cmd record -e all sleep 2
>  trace-cmd report
>
> and make sure that the events report correctly your arch (metag). I just
> want to make sure that it still reads the binary data correctly. I
> should as the /sys/kernel/debug/tracing/events/header_page should
> express the difference in the offset of data.

I just ran trace-cmd on our arch and it appears to work.

# cat /sys/kernel/debug/tracing/events/header_page
        field: u64 timestamp;   offset:0;       size:8; signed:0;
        field: local_t commit;  offset:8;       size:4; signed:1;
        field: int overwrite;   offset:8;       size:1; signed:1;
        field: char data;       offset:16;      size:4080;      signed:0;

Does that look ok?

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 31/40] char: don't build rtc or genrtc on METAG
  2012-11-01 15:39   ` Greg KH
@ 2012-11-01 17:33     ` James Hogan
  2012-11-09 14:38       ` Arnd Bergmann
  0 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-01 17:33 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-arch

On 01/11/12 15:39, Greg KH wrote:
> On Wed, Oct 31, 2012 at 04:14:12PM +0000, James Hogan wrote:
>> The metag architecture doesn't have a PC RTC or implement the RTC
>> interface that genrtc expects, so add METAG to the list of architectures
>> not to build RTC or GEN_RTC on.
>>
>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
>> ---
>>  drivers/char/Kconfig |    5 +++--
>>  1 files changed, 3 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
>> index ea6f632..09b64a8 100644
>> --- a/drivers/char/Kconfig
>> +++ b/drivers/char/Kconfig
>> @@ -283,7 +283,8 @@ if RTC_LIB=n
>>  config RTC
>>  	tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)"
>>  	depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV \
>> -			&& !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN && !UML
>> +			&& !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN \
>> +			&& !UML && !METAG
> 
> This is getting huge, would it be easier to reverse it and just list the
> arches that it does work for?

Hi Greg,

Yes, I think I agree.

I'll probably also squash together all these similar !METAG patches into
one for v2.

Thanks for taking a look
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 04/40] trace/ring_buffer: handle 64bit aligned structs
  2012-11-01 17:32     ` Will Newton
@ 2012-11-01 19:30       ` Steven Rostedt
  0 siblings, 0 replies; 116+ messages in thread
From: Steven Rostedt @ 2012-11-01 19:30 UTC (permalink / raw)
  To: Will Newton; +Cc: James Hogan, linux-arch, Frederic Weisbecker, Ingo Molnar

On Thu, 2012-11-01 at 17:32 +0000, Will Newton wrote:

> > and make sure that the events report correctly your arch (metag). I just
> > want to make sure that it still reads the binary data correctly. I
> > should as the /sys/kernel/debug/tracing/events/header_page should
> > express the difference in the offset of data.
> 
> I just ran trace-cmd on our arch and it appears to work.
> 
> # cat /sys/kernel/debug/tracing/events/header_page
>         field: u64 timestamp;   offset:0;       size:8; signed:0;
>         field: local_t commit;  offset:8;       size:4; signed:1;
>         field: int overwrite;   offset:8;       size:1; signed:1;
>         field: char data;       offset:16;      size:4080;      signed:0;
> 
> Does that look ok?

Yep, it seems fine.

-- Steve

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-01 11:42   ` James Hogan
@ 2012-11-01 23:28     ` Tony Breeds
  2012-11-09 16:52     ` James Hogan
  1 sibling, 0 replies; 116+ messages in thread
From: Tony Breeds @ 2012-11-01 23:28 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, Stephen Rothwell

[-- Attachment #1: Type: text/plain, Size: 636 bytes --]

On Thu, Nov 01, 2012 at 11:42:12AM +0000, James Hogan wrote:

> We're working on a buildroot release with toolchain patches etc
> included, which we hope to make available tomorrow, but which may slip
> to late next week (ELC-E).
> 
> In the mean time, there is an older buildroot available here (which
> requires registration):
> http://www.imgtec.com/minimorph

Thanks.  I'll use that as the source.  Next time I build kernel.org
compilers I'll try to include meta toolchain (for kernel buildsi only).

When you're ready to ask Linus to pull you should get your tree added to
linux-next for a few days.


Yours Tony

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag
  2012-10-31 19:55   ` Myklebust, Trond
  2012-10-31 21:39     ` James Hogan
@ 2012-11-02  9:33     ` James Hogan
  1 sibling, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-02  9:33 UTC (permalink / raw)
  To: Myklebust, Trond; +Cc: linux-arch, Alexander Viro, linux-fsdevel

On 31/10/12 19:55, Myklebust, Trond wrote:
> On Wed, 2012-10-31 at 16:14 +0000, James Hogan wrote:
>> Add the Metag Debug Adapter File System (DAFS), which uses Meta SWITCH
>> operations to communicate with a file server on a host computer via a
>> JTAG debug adapter.
>>
>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
>> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
>> Cc: linux-fsdevel@vger.kernel.org
> 
> You might want to note that DAFS as a name for a remote filesystem is
> already taken:
> 
>   http://en.wikipedia.org/wiki/Direct_Access_File_System

Hi,

Unless there are objections we'll probably rename it to imgdafs in the
next version to disambiguate with Direct Access File System.

Thanks
James


^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-10-31 19:35   ` Sam Ravnborg
  2012-10-31 21:34     ` James Hogan
@ 2012-11-02  9:40     ` James Hogan
  2012-11-02 17:01       ` Sam Ravnborg
  1 sibling, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-02  9:40 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: linux-arch

Hi,

On 31/10/12 19:35, Sam Ravnborg wrote:
> 3) there seems to be confusion if the shorthand is meta or metag.
>    Please fix it up so metag is used everywhere.

The processor cores are called Meta, which should probably be used in
kconfig labels and some text. We then use metag as the "shorthand" name
of the architecture in code, which also makes it a bit less ambiguous. I
tend to rationalise it as being derived from Metagence.

I'll try and make sure the usage of these terms is consistent.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-11-02  9:40     ` James Hogan
@ 2012-11-02 17:01       ` Sam Ravnborg
  0 siblings, 0 replies; 116+ messages in thread
From: Sam Ravnborg @ 2012-11-02 17:01 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Fri, Nov 02, 2012 at 09:40:40AM +0000, James Hogan wrote:
> Hi,
> 
> On 31/10/12 19:35, Sam Ravnborg wrote:
> > 3) there seems to be confusion if the shorthand is meta or metag.
> >    Please fix it up so metag is used everywhere.
> 
> The processor cores are called Meta, which should probably be used in
> kconfig labels and some text. We then use metag as the "shorthand" name
> of the architecture in code, which also makes it a bit less ambiguous. I
> tend to rationalise it as being derived from Metagence.

Thanks for looking into this!

In general people will recognize it if you use the same shorthand as
the arch dir. So if you decide for arch/metag then for example
all Kconfig symbols should use the METAG_ prefix.
Also if theyare dealing with the core.

This will confuse the naive reader less.

	Sam

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 14/40] metag: Ptrace
  2012-10-31 16:13 ` [RFC PATCH v1 14/40] metag: Ptrace James Hogan
@ 2012-11-08  7:17   ` Jonas Bonn
  2012-11-09  9:26     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Jonas Bonn @ 2012-11-08  7:17 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On 31 October 2012 17:13, James Hogan <james.hogan@imgtec.com> wrote:
> We use the non-generic interface at the moment (exposing the TBX context
> structures to userland), pending conversion to the generic ptrace
> interface.
>

Hi James,

Comment 1, as you already mentioned in the blurb, is to use the
generic ptrace interface... a lot of your ptrace functions aren't need
anymore.
Comment 2, is to avoid exporting the struct pt_regs to userspace.  GDB
should be using regsets for accessing the current context for new
architectures and doesn't need to know about the pt_regs layout
anymore.  Not exporting pt_regs gains you the flexibility to optimize
context saving in the future if you figure out a better (e.g. cache
friendlier) way of saving your registers at exception time.

/Jonas

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 14/40] metag: Ptrace
  2012-11-08  7:17   ` Jonas Bonn
@ 2012-11-09  9:26     ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-09  9:26 UTC (permalink / raw)
  To: Jonas Bonn; +Cc: linux-arch

On 08/11/12 07:17, Jonas Bonn wrote:
> On 31 October 2012 17:13, James Hogan <james.hogan@imgtec.com> wrote:
>> We use the non-generic interface at the moment (exposing the TBX context
>> structures to userland), pending conversion to the generic ptrace
>> interface.
>>
> 
> Hi James,
> 
> Comment 1, as you already mentioned in the blurb, is to use the
> generic ptrace interface... a lot of your ptrace functions aren't need
> anymore.
> Comment 2, is to avoid exporting the struct pt_regs to userspace.  GDB
> should be using regsets for accessing the current context for new
> architectures and doesn't need to know about the pt_regs layout
> anymore.  Not exporting pt_regs gains you the flexibility to optimize
> context saving in the future if you figure out a better (e.g. cache
> friendlier) way of saving your registers at exception time.

Thanks Jonas,

Yes, this is definitely something we want to do as our pt_regs layout is
rather convoluted and we'd like the flexibility to simplify it.

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 17/40] metag: IRQ handling
  2012-10-31 16:13 ` [RFC PATCH v1 17/40] metag: IRQ handling James Hogan
@ 2012-11-09 14:12   ` Arnd Bergmann
  2012-11-20 16:08     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 14:12 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wednesday 31 October 2012, James Hogan wrote:

> diff --git a/arch/metag/include/asm/irq.h b/arch/metag/include/asm/irq.h
> new file mode 100644
> index 0000000..da24886
> --- /dev/null
> +++ b/arch/metag/include/asm/irq.h
> +#ifndef HW_IRQS
> +#define HW_IRQS  (HWSTATMETA_OFFSET_MAX + HWSTATEXT_OFFSET_MAX + \
> +		HWSTATEXT2_OFFSET_MAX + HWSTATEXT4_OFFSET_MAX + \
> +		HWSTATEXT6_OFFSET_MAX)
> +#endif
> +
> +#define META_IRQS 32
> +
> +#define NR_IRQS (META_IRQS + HW_IRQS)

I think you should use sparse IRQs right from the start and avoid
hardcoding interrupt numbers from the start.

> +/**
> + * meta_intc_stat_addr() - Get the address of a HWSTATEXT register
> + * @hw:		Hardware IRQ number (within external trigger block)
> + *
> + * Returns:	Address of a HWSTATEXT register containing the status bit for
> + *		the specified hardware IRQ number
> + */
> +static void __iomem *meta_intc_stat_addr(irq_hw_number_t hw)
> +{
> +	return (void __iomem *)(HWSTATEXT +
> +				HWSTAT_STRIDE * meta_intc_bank(hw));
> +}

Hardcoding register addresses like this is considered bad style, even for
an interrupt controller. Better use ioremap to initialize a pointer
dynamically at boot time.

> +struct irq_chip meta_intc_edge_chip = {
> +	.irq_startup		= meta_intc_startup_irq,
> +	.irq_shutdown		= meta_intc_shutdown_irq,
> +	.irq_ack		= meta_intc_ack_irq,
> +	.irq_mask		= meta_intc_mask_irq,
> +	.irq_unmask		= meta_intc_unmask_irq,
> +	.irq_set_type		= meta_intc_irq_set_type,
> +	.irq_set_affinity	= meta_intc_set_affinity,
> +	.flags			= META_INTC_CHIP_FLAGS,
> +};
> +
> +struct irq_chip meta_intc_level_chip = {
> +	.irq_startup		= meta_intc_startup_irq,
> +	.irq_shutdown		= meta_intc_shutdown_irq,
> +	.irq_set_type		= meta_intc_irq_set_type,
> +	.irq_mask		= meta_intc_mask_irq,
> +	.irq_unmask		= meta_intc_unmask_irq,
> +	.irq_set_affinity	= meta_intc_set_affinity,
> +	.flags			= META_INTC_CHIP_FLAGS,
> +};

I would recommend trying to put the external irq_chip code into
drivers/irqchip, which we are currently establishing. Right now,
that directory is still a bit dependent on the ARM architecture,
but IMHO that is an even stronger reason to use it for a new
architecture, so we can make it arch independent in the process,
without the fear of breaking existing users. After it works
on multiple architectures, we can start migrating the others.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 18/40] metag: System Calls
  2012-10-31 16:13 ` [RFC PATCH v1 18/40] metag: System Calls James Hogan
@ 2012-11-09 14:20   ` Arnd Bergmann
  2012-11-22 12:01     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 14:20 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wednesday 31 October 2012, James Hogan wrote:

> +/* kernel/signal.c */
> +asmlinkage int sys_sigaltstack(const stack_t __user *, stack_t __user *,
> +			       unsigned long, unsigned long,
> +			       unsigned long, unsigned long,
> +			       struct pt_regs *);
> +asmlinkage int sys_rt_sigreturn(unsigned long, unsigned long,
> +				unsigned long, unsigned long,
> +				unsigned long, unsigned long,
> +				struct pt_regs *);
> +asmlinkage long sys_rt_sigaction(int sig,
> +				 const struct sigaction __user *act,
> +				 struct sigaction __user *oact,
> +				 size_t sigsetsize);
> +asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset,
> +				  size_t sigsetsize);
> +asmlinkage long sys_rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t __user *);

Can't you use the declarations from asm-generic/syscalls.h for these?

>> +asmlinkage long sys32_truncate64(const char __user *, unsigned long,
> +				 unsigned long);
> +asmlinkage long sys32_ftruncate64(unsigned int, unsigned long,
> +				  unsigned long);
> +asmlinkage long sys32_fadvise64_64(int, unsigned long, unsigned long,
> +				   unsigned long, unsigned long, int);
> +asmlinkage long sys32_readahead(int, unsigned long, unsigned long, size_t);
> +asmlinkage ssize_t sys32_pread64(unsigned long, char __user *, size_t,
> +				 unsigned long, unsigned long);
> +asmlinkage ssize_t sys32_pwrite64(unsigned long, char __user *, size_t,
> +				  unsigned long, unsigned long);
> +asmlinkage long sys32_sync_file_range(int, unsigned long, unsigned long,
> +				      unsigned long, unsigned long,
> +				      unsigned int);

These confused me a bit. The sys32_* notation is traditionally used for
32 bit compatibility functions on 64 bit architectures. Maybe it's better
to name these sys_meta_* instead.

> +asmlinkage int sys_metag_spinlock(int __user *spinlock)
> +{
> +	int ret = 0, tmp;
> +
> +	local_irq_disable();
> +	get_user(tmp, spinlock);
> +	if (tmp)
> +		ret = 1;
> +	tmp = 1;
> +	put_user(tmp, spinlock);
> +	local_irq_enable();
> +	return ret;
> +}

This seems to be a specialization of a cmpxchg() function. How about
implementing cmpxchg semantics instead? It should be more flexible
for almost the same runtime cost.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
  2012-10-31 16:14 ` [RFC PATCH v1 24/40] metag: DMA James Hogan
@ 2012-11-09 14:25   ` Arnd Bergmann
  2012-11-23 15:53     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 14:25 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wednesday 31 October 2012, James Hogan wrote:
> +static inline void
> +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
> +                enum dma_data_direction direction)
> +{
> +       BUG_ON(!valid_dma_direction(direction));
> +       dma_sync_for_cpu((void *)bus_to_virt(dma_addr), size, direction);
> +}

bus_to_virt is deprecated an should not be visible to device drivers
any more for new stuff. Maybe you can remove the definition and introduce
a __bus_to_virt() function for internal use that also returns a pointer
type instead.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 32/40] i8042: don't build on METAG
  2012-10-31 16:14 ` [RFC PATCH v1 32/40] i8042: don't build " James Hogan
@ 2012-11-09 14:28   ` Arnd Bergmann
  2012-11-16 11:06     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 14:28 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wednesday 31 October 2012, James Hogan wrote:
> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> index 55f2c22..60b2a09 100644
> --- a/drivers/input/serio/Kconfig
> +++ b/drivers/input/serio/Kconfig
> @@ -22,7 +22,7 @@ config SERIO_I8042
>         tristate "i8042 PC Keyboard controller" if EXPERT || !X86
>         default y
>         depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
> -                  (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
> +                  (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !METAG
>         help
>           i8042 is the chip over which the standard AT keyboard and PS/2
>           mouse are connected to the computer. If you use these devices,

Maybe you can turn that list around and introduce a new symbol
ARCH_HAS_SERIO_I8042 or similar that architectures can select.
I suspect that actually most don't have this, but they have not
bothered to update this list.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 37/40] metag: Various sysfs drivers
  2012-10-31 19:41     ` James Hogan
@ 2012-11-09 14:32       ` Arnd Bergmann
  2012-11-09 14:35         ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 14:32 UTC (permalink / raw)
  To: James Hogan; +Cc: Greg KH, linux-arch

On Wednesday 31 October 2012, James Hogan wrote:
> 
> On 31/10/12 19:30, Greg KH wrote:
> > On Wed, Oct 31, 2012 at 04:14:18PM +0000, James Hogan wrote:
> >> Add various Meta specific sysfs drivers.
> >>
> >> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> > 
> > As you are creating new sysfs files, please also create new
> > Documentation/ABI/ files describing what those sysfs files do at the
> > same time.
> 
> Good point, I'll make sure Documentation is added.
> 

While I haven't looked at the contents of these files in much detail,
I suspect that they are not actually sysfs material at all. Anything
related to performance counters should probably go into "perf".

You can also have your own debugfs directories if that is not a good
fit, but when these are mainly debugging helps, debugfs seems more
appropriate than sysfs.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 37/40] metag: Various sysfs drivers
  2012-11-09 14:32       ` Arnd Bergmann
@ 2012-11-09 14:35         ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-09 14:35 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: Greg KH, linux-arch

On 09/11/12 14:32, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
>>
>> On 31/10/12 19:30, Greg KH wrote:
>>> On Wed, Oct 31, 2012 at 04:14:18PM +0000, James Hogan wrote:
>>>> Add various Meta specific sysfs drivers.
>>>>
>>>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
>>>
>>> As you are creating new sysfs files, please also create new
>>> Documentation/ABI/ files describing what those sysfs files do at the
>>> same time.
>>
>> Good point, I'll make sure Documentation is added.
>>
> 
> While I haven't looked at the contents of these files in much detail,
> I suspect that they are not actually sysfs material at all. Anything
> related to performance counters should probably go into "perf".
> 
> You can also have your own debugfs directories if that is not a good
> fit, but when these are mainly debugging helps, debugfs seems more
> appropriate than sysfs.

Yes, I'm planning on dropping these from the core patchset as we never
use them ourselves so they're not a priority.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 31/40] char: don't build rtc or genrtc on METAG
  2012-11-01 17:33     ` James Hogan
@ 2012-11-09 14:38       ` Arnd Bergmann
  0 siblings, 0 replies; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 14:38 UTC (permalink / raw)
  To: James Hogan; +Cc: Greg KH, linux-arch

On Thursday 01 November 2012, James Hogan wrote:
> On 01/11/12 15:39, Greg KH wrote:
> > On Wed, Oct 31, 2012 at 04:14:12PM +0000, James Hogan wrote:
> >> The metag architecture doesn't have a PC RTC or implement the RTC
> >> interface that genrtc expects, so add METAG to the list of architectures
> >> not to build RTC or GEN_RTC on.
> >>
> >> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> >> ---
> >>  drivers/char/Kconfig |    5 +++--
> >>  1 files changed, 3 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> >> index ea6f632..09b64a8 100644
> >> --- a/drivers/char/Kconfig
> >> +++ b/drivers/char/Kconfig
> >> @@ -283,7 +283,8 @@ if RTC_LIB=n
> >>  config RTC
> >>      tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)"
> >>      depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV \
> >> -                    && !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN && !UML
> >> +                    && !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN \
> >> +                    && !UML && !METAG
> > 
> > This is getting huge, would it be easier to reverse it and just list the
> > arches that it does work for?
> 
> Hi Greg,
> 
> Yes, I think I agree.
> 
> I'll probably also squash together all these similar !METAG patches into
> one for v2.

Since I made a similar comment for another patch, and I now saw that you
have more of these, let's start to rethink this from teh beginning.

We have a bunch of "PC-style" I/O drivers: parport_pc, vga, i8042,
legacy-rtc, floppy and maybe some more. these usually come together
or not at all, so I think it would be good to have a CONFIG_LEGACY_PC_IO
symbol that all architectures select if they have the majority of the
above, and then we can use variants of the other symbols for the
exceptions.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-10-31 16:13 ` [RFC PATCH v1 12/40] metag: Build infrastructure James Hogan
  2012-10-31 19:35   ` Sam Ravnborg
@ 2012-11-09 14:46   ` Arnd Bergmann
  2012-11-09 14:55     ` James Hogan
  2012-11-20 15:06     ` James Hogan
  1 sibling, 2 replies; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 14:46 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wednesday 31 October 2012, James Hogan wrote:
> +
> +define archhelp
> +  echo  '* vmlinux.bin	- Binary kernel image (arch/$(ARCH)/boot/vmlinux.bin)'
> +  @echo '  uImage  	- Alias to bootable U-Boot image'
> +  @echo '  uImage.bin	- Kernel-only image for U-Boot (bin)'
> +  @echo '  uImage.gz	- Kernel-only image for U-Boot (gzip)'
> +  @echo '  uImage.bz2	- Kernel-only image for U-Boot (bzip2)'
> +  @echo '  uImage.lzma	- Kernel-only image for U-Boot (lzma)'
> +  @echo '  uImage.xz	- Kernel-only image for U-Boot (xz)'
> +  @echo '  uImage.lzo	- Kernel-only image for U-Boot (lzo)'
> +endef

The uImage format is causing some trouble on ARM because it requires knowing
the load address at boot time, so we try to move people towards using just
zImage files there. You might want to consider the same.

It's probably not necessary to have both lzma and xz formats since they
are the same algorithm with different headers afaik.

> diff --git a/arch/metag/include/asm/Kbuild b/arch/metag/include/asm/Kbuild
> new file mode 100644
> index 0000000..1678d85
> --- /dev/null
> +++ b/arch/metag/include/asm/Kbuild
> @@ -0,0 +1,59 @@
> +include include/asm-generic/Kbuild.asm
> +
> +header-y += tbx/
> +
> +header-y += hw_breakpoints.h
> +
> +generic-y += auxvec.h
> +generic-y += bitsperlong.h
> +generic-y += bugs.h
> +generic-y += cputime.h
> +generic-y += current.h

The header files will have to change now for the UAPI split, see the discussion
about the same thing for the ARC architecture.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-11-09 14:46   ` Arnd Bergmann
@ 2012-11-09 14:55     ` James Hogan
  2012-11-20 15:06     ` James Hogan
  1 sibling, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-09 14:55 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 14:46, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
>> diff --git a/arch/metag/include/asm/Kbuild b/arch/metag/include/asm/Kbuild
>> new file mode 100644
>> index 0000000..1678d85
>> --- /dev/null
>> +++ b/arch/metag/include/asm/Kbuild
>> @@ -0,0 +1,59 @@
>> +include include/asm-generic/Kbuild.asm
>> +
>> +header-y += tbx/
>> +
>> +header-y += hw_breakpoints.h
>> +
>> +generic-y += auxvec.h
>> +generic-y += bitsperlong.h
>> +generic-y += bugs.h
>> +generic-y += cputime.h
>> +generic-y += current.h
> 
> The header files will have to change now for the UAPI split, see the discussion
> about the same thing for the ARC architecture.

Yes, I've managed to do the UAPI split now (thanks to some help from
David Howells), so the next version will be based on at least v3.7-rc4
and have the split combined in.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-10-31 16:26 ` [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
@ 2012-11-09 15:06   ` Arnd Bergmann
  2012-11-09 15:21     ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 15:06 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wednesday 31 October 2012, James Hogan wrote:
> Apologies. It appears the following two patches were too large and got
> blocked. I'll split them up. In the mean time, here are github links:
> >   metag: Memory management
> 
> https://github.com/jahogan/metag-linux/commit/34c0fe0d6ab61f83a56f9e9d3ea04adddaa7a1d4
> 
pasting my comments here:

>diff --git a/arch/metag/include/asm/io.h b/arch/metag/include/asm/io.h
>new file mode 100644
>index 0000000..d67adb3
>--- /dev/null
>+++ b/arch/metag/include/asm/io.h
>@@ -0,0 +1,96 @@
>+#ifndef _ASM_METAG_IO_H
>+#define _ASM_METAG_IO_H
>+
>+#ifdef __KERNEL__
>+
>+#define IO_SPACE_LIMIT  0xFFFFFFFF

You should make this one 0 if you have no PCI I/O space. If you have it, it
should be the actual size of the window.

>+#define virt_to_bus virt_to_phys
>+#define bus_to_virt phys_to_virt

As mentioned in another patch, I would recommend removing these and defining
ARCH_NO_VIRT_TO_BUS in Kconfig.

>+ * Despite being a 32bit architecture, Meta can do 64bit memory accesses
>+ * (assuming the bus supports it).
>+ */
>+
>+static inline u64 __raw_readq(const volatile void __iomem *addr)
>+{
>+	return *(const volatile u64 __force *) addr;
>+}
>+#define readq(addr) __raw_readq(addr)
>+
>+static inline void __raw_writeq(u64 b, volatile void __iomem *addr)
>+{
>+	*(volatile u64 __force *) addr = b;
>+}
>+#define writeq(b, addr) __raw_writeq(b, addr)

These should be using an inline assembly to guarantee that it gets
turned into an atomic access, at least of the architecture has
an atomic 64-bit load/store from memory operation.

>+/*
>+ * A load of the architecture code uses read/write functions with raw physical
>+ * address numbers rather than __iomem pointers. Until these are fixed, do the
>+ * cast here to hide the warnings.
>+ */
>+
>+#undef readb
>+#undef readw
>+#undef readl
>+#undef readq
>+#define readb(addr)	__raw_readb((volatile void __iomem *)(addr))
>+#define readw(addr)	__raw_readw((volatile void __iomem *)(addr))
>+#define readl(addr)	__raw_readl((volatile void __iomem *)(addr))
>+#define readq(addr)	__raw_readq((volatile void __iomem *)(addr))

No, fix the drivers instead. We finally did the same thing on ARM,
so all common upstream drivers should be clean now. As for atomic accesses,
the same comment as above applies, so use inline assembly here.

You can of course keep an out-of-tree patch that puts the type casts
back here to help migrating any out-of-tree device drivers, just make
sure that whatever you merge upstream does it right.

>diff --git a/arch/metag/mm/mmu-meta1.c b/arch/metag/mm/mmu-meta1.c
>new file mode 100644
>index 0000000..5e1f7d8
>--- /dev/null
>+++ b/arch/metag/mm/mmu-meta1.c
>@@ -0,0 +1,157 @@
>+/*
>+ *  Copyright (C) 2005,2006,2007,2008,2009 Imagination Technologies
>+ *
>+ * Meta 1 MMU handling code.
>+ *
>+ */
>+
>+#include <linux/sched.h>
>+#include <linux/mm.h>
>+#include <linux/io.h>
>+
>+#include <asm/mmu.h>
>+
>+#define DM3_BASE (LINSYSDIRECT_BASE + (MMCU_DIRECTMAPn_ADDR_SCALE * 3))

Again, don't hardcode MMIO addresses like this, but instead get them
from the device tree to make the code more portable to future systems.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
                   ` (39 preceding siblings ...)
  2012-10-31 22:33 ` Tony Breeds
@ 2012-11-09 15:16 ` Arnd Bergmann
  2012-11-09 16:06   ` James Hogan
  40 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 15:16 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Wednesday 31 October 2012, James Hogan wrote:
> This patchset adds core architecture support to Linux for Imagination's
> Meta ATP (Meta 1) and HTP (Meta 2) processor cores. It is primarily an
> RFC patchset to get some early feedback about what changes we need to
> make, although of course all feedback is most welcome.
> 
> The patches are based on v3.6 for now, and can also be found in the
> following git tree:
>   git://github.com/jahogan/metag-linux.git metag-core
> 
> Meta cores are 32-bit, hardware multithreaded, general purpose, embedded
> processors which also feature a DSP instruction set, and can be found in
> many digital radios. They are capable of running different operating
> systems on different hardware threads, for example a digital radio might
> run RTOSes for DAB decoding and audio decoding on 3 hardware threads,
> and run Linux on the 4th hardware thread to manage the user interface,
> networking etc. HTPs are also capable of running SMP Linux on multiple
> hardware threads.
> 
> Instruction set and architecture overview documents can be found on the
> following page (these currently require registration/NDA, a restriction
> which was not intended and will shortly be removed):
>   http://www.imgtec.com/downloads.asp
> 

Hi James,

I'm finished with my initial review now. The code all looks reasonable
so far, I could find no show-stoppers and only had small comments.

What mainly seems to be missing here is platform specific support and
device drivers.  You are using the flattened device tree, which is good,
but don't have any example device tree files or new drivers, which makes
me wonder how you test the code ;-)

What are your plans around those?
I assume that the drivers are not ready yet and will follow later.

Do you expect a lot of device drivers to be shared with MIPS and ARM
based systems?

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-09 15:06   ` Arnd Bergmann
@ 2012-11-09 15:21     ` James Hogan
  2012-11-09 15:58       ` Arnd Bergmann
  0 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-09 15:21 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 15:06, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
>> Apologies. It appears the following two patches were too large and got
>> blocked. I'll split them up. In the mean time, here are github links:
>>>   metag: Memory management
>>
>> https://github.com/jahogan/metag-linux/commit/34c0fe0d6ab61f83a56f9e9d3ea04adddaa7a1d4
>>
> pasting my comments here:
> 
>> diff --git a/arch/metag/include/asm/io.h b/arch/metag/include/asm/io.h
>> new file mode 100644
>> index 0000000..d67adb3
>> --- /dev/null
>> +++ b/arch/metag/include/asm/io.h
>> @@ -0,0 +1,96 @@
>> +#ifndef _ASM_METAG_IO_H
>> +#define _ASM_METAG_IO_H
>> +
>> +#ifdef __KERNEL__
>> +
>> +#define IO_SPACE_LIMIT  0xFFFFFFFF
> 
> You should make this one 0 if you have no PCI I/O space. If you have it, it
> should be the actual size of the window.

Okay, thanks

> 
>> +#define virt_to_bus virt_to_phys
>> +#define bus_to_virt phys_to_virt
> 
> As mentioned in another patch, I would recommend removing these and defining
> ARCH_NO_VIRT_TO_BUS in Kconfig.

Okay, thanks

>> + * Despite being a 32bit architecture, Meta can do 64bit memory accesses
>> + * (assuming the bus supports it).
>> + */
>> +
>> +static inline u64 __raw_readq(const volatile void __iomem *addr)
>> +{
>> +	return *(const volatile u64 __force *) addr;
>> +}
>> +#define readq(addr) __raw_readq(addr)
>> +
>> +static inline void __raw_writeq(u64 b, volatile void __iomem *addr)
>> +{
>> +	*(volatile u64 __force *) addr = b;
>> +}
>> +#define writeq(b, addr) __raw_writeq(b, addr)
> 
> These should be using an inline assembly to guarantee that it gets
> turned into an atomic access, at least of the architecture has
> an atomic 64-bit load/store from memory operation.

Is there a particular case you have in mind where the compiler could be
expected not to generate an atomic memory op (I presume you mean e.g. 2
32bit memory ops)? These are implemented the same as asm-generic/io.h
which only omits these ones because metag is 32bit.

tbh I don't think the 64 bit accesses are ever actually used, so we
could probably drop these ones.

> 
>> +/*
>> + * A load of the architecture code uses read/write functions with raw physical
>> + * address numbers rather than __iomem pointers. Until these are fixed, do the
>> + * cast here to hide the warnings.
>> + */
>> +
>> +#undef readb
>> +#undef readw
>> +#undef readl
>> +#undef readq
>> +#define readb(addr)	__raw_readb((volatile void __iomem *)(addr))
>> +#define readw(addr)	__raw_readw((volatile void __iomem *)(addr))
>> +#define readl(addr)	__raw_readl((volatile void __iomem *)(addr))
>> +#define readq(addr)	__raw_readq((volatile void __iomem *)(addr))
> 
> No, fix the drivers instead. We finally did the same thing on ARM,
> so all common upstream drivers should be clean now. As for atomic accesses,
> the same comment as above applies, so use inline assembly here.

same again, does that mean asm-generic/io.h should never be used?

> You can of course keep an out-of-tree patch that puts the type casts
> back here to help migrating any out-of-tree device drivers, just make
> sure that whatever you merge upstream does it right.

Okay, agreed.

>> diff --git a/arch/metag/mm/mmu-meta1.c b/arch/metag/mm/mmu-meta1.c
>> new file mode 100644
>> index 0000000..5e1f7d8
>> --- /dev/null
>> +++ b/arch/metag/mm/mmu-meta1.c
>> @@ -0,0 +1,157 @@
>> +/*
>> + *  Copyright (C) 2005,2006,2007,2008,2009 Imagination Technologies
>> + *
>> + * Meta 1 MMU handling code.
>> + *
>> + */
>> +
>> +#include <linux/sched.h>
>> +#include <linux/mm.h>
>> +#include <linux/io.h>
>> +
>> +#include <asm/mmu.h>
>> +
>> +#define DM3_BASE (LINSYSDIRECT_BASE + (MMCU_DIRECTMAPn_ADDR_SCALE * 3))
> 
> Again, don't hardcode MMIO addresses like this, but instead get them
> from the device tree to make the code more portable to future systems.

Even if the addresses are part of the core architecture? I guess DT
could provide the blocks of registers, but it seems like unnecessary
overhead for something that will never change.

Thanks for taking a look.

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-09 15:21     ` James Hogan
@ 2012-11-09 15:58       ` Arnd Bergmann
  2012-11-09 16:20         ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 15:58 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Friday 09 November 2012, James Hogan wrote:
> On 09/11/12 15:06, Arnd Bergmann wrote:
> > On Wednesday 31 October 2012, James Hogan wrote:

> >> + * Despite being a 32bit architecture, Meta can do 64bit memory accesses
> >> + * (assuming the bus supports it).
> >> + */
> >> +
> >> +static inline u64 __raw_readq(const volatile void __iomem *addr)
> >> +{
> >> +	return *(const volatile u64 __force *) addr;
> >> +}
> >> +#define readq(addr) __raw_readq(addr)
> >> +
> >> +static inline void __raw_writeq(u64 b, volatile void __iomem *addr)
> >> +{
> >> +	*(volatile u64 __force *) addr = b;
> >> +}
> >> +#define writeq(b, addr) __raw_writeq(b, addr)
> > 
> > These should be using an inline assembly to guarantee that it gets
> > turned into an atomic access, at least of the architecture has
> > an atomic 64-bit load/store from memory operation.
> 
> Is there a particular case you have in mind where the compiler could be
> expected not to generate an atomic memory op (I presume you mean e.g. 2
> 32bit memory ops)? These are implemented the same as asm-generic/io.h
> which only omits these ones because metag is 32bit.
> 
> tbh I don't think the 64 bit accesses are ever actually used, so we
> could probably drop these ones.

There is nothing forcing a compiler to turn a dereferece of a volatile
pointer into a single load or store. We've had issues in the past
where a such an access got turned into a series of byte accesses
when an mmio data structure gets marked as '__packed', but it could
happen in other occasions as well.

> >> +/*
> >> + * A load of the architecture code uses read/write functions with raw physical
> >> + * address numbers rather than __iomem pointers. Until these are fixed, do the
> >> + * cast here to hide the warnings.
> >> + */
> >> +
> >> +#undef readb
> >> +#undef readw
> >> +#undef readl
> >> +#undef readq
> >> +#define readb(addr)	__raw_readb((volatile void __iomem *)(addr))
> >> +#define readw(addr)	__raw_readw((volatile void __iomem *)(addr))
> >> +#define readl(addr)	__raw_readl((volatile void __iomem *)(addr))
> >> +#define readq(addr)	__raw_readq((volatile void __iomem *)(addr))
> > 
> > No, fix the drivers instead. We finally did the same thing on ARM,
> > so all common upstream drivers should be clean now. As for atomic accesses,
> > the same comment as above applies, so use inline assembly here.
> 
> same again, does that mean asm-generic/io.h should never be used?

It's a reasonable default for bringup, but in general I would recommend
overriding these accessors. The rest of asm-generic/io.h is largely ok.

> >> +#define DM3_BASE (LINSYSDIRECT_BASE + (MMCU_DIRECTMAPn_ADDR_SCALE * 3))
> > 
> > Again, don't hardcode MMIO addresses like this, but instead get them
> > from the device tree to make the code more portable to future systems.
> 
> Even if the addresses are part of the core architecture? I guess DT
> could provide the blocks of registers, but it seems like unnecessary
> overhead for something that will never change.

The physical addresses might be part of the architecture, but it
looks like you are hardcoding the virtual address here, which is
the result of an ioremap operation or something similar (I have
not looked at this example). It's probably fine to put it into
fixmap if that provides a real advantage.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-09 15:16 ` Arnd Bergmann
@ 2012-11-09 16:06   ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-09 16:06 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 15:16, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
>> This patchset adds core architecture support to Linux for Imagination's
>> Meta ATP (Meta 1) and HTP (Meta 2) processor cores. It is primarily an
>> RFC patchset to get some early feedback about what changes we need to
>> make, although of course all feedback is most welcome.
>>
>> The patches are based on v3.6 for now, and can also be found in the
>> following git tree:
>>   git://github.com/jahogan/metag-linux.git metag-core
>>
>> Meta cores are 32-bit, hardware multithreaded, general purpose, embedded
>> processors which also feature a DSP instruction set, and can be found in
>> many digital radios. They are capable of running different operating
>> systems on different hardware threads, for example a digital radio might
>> run RTOSes for DAB decoding and audio decoding on 3 hardware threads,
>> and run Linux on the 4th hardware thread to manage the user interface,
>> networking etc. HTPs are also capable of running SMP Linux on multiple
>> hardware threads.
>>
>> Instruction set and architecture overview documents can be found on the
>> following page (these currently require registration/NDA, a restriction
>> which was not intended and will shortly be removed):
>>   http://www.imgtec.com/downloads.asp
>>
> 
> Hi James,
> 
> I'm finished with my initial review now. The code all looks reasonable
> so far, I could find no show-stoppers and only had small comments.

Thanks for reviewing :-)

> What mainly seems to be missing here is platform specific support and
> device drivers.  You are using the flattened device tree, which is good,
> but don't have any example device tree files or new drivers, which makes
> me wonder how you test the code ;-)

We maintain some support for a couple of SoCs, a couple dozen SoC
peripherals, and around 7 boards which we're still in the process of
cleaning up and converting to devicetree and the relevant newer APIs
(pinmux, common clocks framework etc), so we have some confidence that
IRQs and basic device tree stuff is working.

> What are your plans around those?
> I assume that the drivers are not ready yet and will follow later.

Basically submit them as soon as we've got them ready.

> Do you expect a lot of device drivers to be shared with MIPS and ARM
> based systems?

Certainly some, and for others it's always a possibility. Will's already
the dw_mmc maintainer, and we also have a partially cleaned up version
of the dwc_otg driver which I think is in the Raspberry Pi. We also make
use of various in-tree drivers, and have a fair few drivers for IMG
hardware blocks that could potentially be used with other architectures
in the future.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-09 15:58       ` Arnd Bergmann
@ 2012-11-09 16:20         ` James Hogan
  2012-11-09 21:55           ` Arnd Bergmann
  2012-11-12 16:59           ` James Hogan
  0 siblings, 2 replies; 116+ messages in thread
From: James Hogan @ 2012-11-09 16:20 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 15:58, Arnd Bergmann wrote:
> On Friday 09 November 2012, James Hogan wrote:
>> On 09/11/12 15:06, Arnd Bergmann wrote:
>>> On Wednesday 31 October 2012, James Hogan wrote:
> 
>>>> + * Despite being a 32bit architecture, Meta can do 64bit memory accesses
>>>> + * (assuming the bus supports it).
>>>> + */
>>>> +
>>>> +static inline u64 __raw_readq(const volatile void __iomem *addr)
>>>> +{
>>>> +	return *(const volatile u64 __force *) addr;
>>>> +}
>>>> +#define readq(addr) __raw_readq(addr)
>>>> +
>>>> +static inline void __raw_writeq(u64 b, volatile void __iomem *addr)
>>>> +{
>>>> +	*(volatile u64 __force *) addr = b;
>>>> +}
>>>> +#define writeq(b, addr) __raw_writeq(b, addr)
>>>
>>> These should be using an inline assembly to guarantee that it gets
>>> turned into an atomic access, at least of the architecture has
>>> an atomic 64-bit load/store from memory operation.
>>
>> Is there a particular case you have in mind where the compiler could be
>> expected not to generate an atomic memory op (I presume you mean e.g. 2
>> 32bit memory ops)? These are implemented the same as asm-generic/io.h
>> which only omits these ones because metag is 32bit.
>>
>> tbh I don't think the 64 bit accesses are ever actually used, so we
>> could probably drop these ones.
> 
> There is nothing forcing a compiler to turn a dereferece of a volatile
> pointer into a single load or store. We've had issues in the past
> where a such an access got turned into a series of byte accesses
> when an mmio data structure gets marked as '__packed', but it could
> happen in other occasions as well.

Okay fair enough. We'll use inline asm here instead.

>>>> +#define DM3_BASE (LINSYSDIRECT_BASE + (MMCU_DIRECTMAPn_ADDR_SCALE * 3))
>>>
>>> Again, don't hardcode MMIO addresses like this, but instead get them
>>> from the device tree to make the code more portable to future systems.
>>
>> Even if the addresses are part of the core architecture? I guess DT
>> could provide the blocks of registers, but it seems like unnecessary
>> overhead for something that will never change.
> 
> The physical addresses might be part of the architecture, but it
> looks like you are hardcoding the virtual address here, which is
> the result of an ioremap operation or something similar (I have
> not looked at this example). It's probably fine to put it into
> fixmap if that provides a real advantage.

Ah right, I see what you're getting at. Meta's virtual address space is
a little non-conventional, which I think makes our approach valid:

0x00000000-0x07ffffff and 0x80000000-0x87ffffff
non-mmuable, always directly maps to physical addresses. These contains
core registers, the io bus where SoC peripherals tend to go (so for
generic peripherals in this region we do use ioremap), and core memories
(same latency as L1 cache).

0x08000000-0x7fffffff and 0x88000000-0xffffffff
mmuable memory (but can't map the non-mmuable region)

Therefore our ioremap() returns the input address for the non-mmuable
regions, and creates an MMU mapping otherwise. So we could ioremap these
addresses, but for such metag specific code it's faster and less
complicated not to bother.

Unfortunately this is also part of the reason we have a prevalence of
readl/writel warnings when we remove the cast in io.h.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-01 11:42   ` James Hogan
  2012-11-01 23:28     ` Tony Breeds
@ 2012-11-09 16:52     ` James Hogan
  1 sibling, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-09 16:52 UTC (permalink / raw)
  To: Tony Breeds; +Cc: linux-arch

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 01/11/12 11:42, James Hogan wrote:
> On 31/10/12 22:33, Tony Breeds wrote:
>> On Wed, Oct 31, 2012 at 04:13:41PM +0000, James Hogan wrote:
>>> This patchset adds core architecture support to Linux for 
>>> Imagination's Meta ATP (Meta 1) and HTP (Meta 2) processor
>>> cores. It is primarily an RFC patchset to get some early
>>> feedback about what changes we need to make, although of course
>>> all feedback is most welcome.
> 
>> Any chance there are patches out there to build a toolchain?
> 
> Hi Tony,
> 
> We're working on a buildroot release with toolchain patches etc 
> included, which we hope to make available tomorrow, but which may
> slip to late next week (ELC-E).

Hi,

A buildroot git tree with gcc, uClibc, and kernel header patches is
now available here:
git://github.com/img-meta/metag-buildroot.git

Use the meta2_defconfig.

Cheers
James
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iQIcBAEBAgAGBQJQnTS3AAoJEKHZs+irPybfL5MQAKNj1yK7H9PKB8ABS0plN/4i
OW3GUcBjUiq4tk/avo2TGKVPrrw8b/d0do7bIyliucPKIlf9J71Ci3NMsaudvKID
d1d6Jsz9y0qw7cBhe2wrcoAVvOCufuepV79OxJe6Z2uIhFZrwhaEbPnjraauswIp
L0bEQYJpvpb8k5l0Lx6LH6nhGSkCZCGt5NeVkESv5idnbkcBEJjWhs4o3yEvA/al
3Ceng1zwnjNPkdl5cJe7Zgsif6rcYxqVIMXs7mmMV0OGknHYD1qX+LBtFLkPiuU/
nVuenVDfIJY+S+D3UFOgBd8r/FxpMZ5EC7S0yVETs3qptuaDyBMbt3O6+TQRZPD3
AKQBi1tEpcG3AYTayp/2QkiXJwctYL3wgUuH4PgumxR6lP4X0dQWoLINZs1UJ61c
TgGX8MHgXNaLWwGpmT9kHAZxmDtwh9hPfR+hMx/BHm8jW3B5LU+3OG2OnMfgBh6V
RHaomIQsY93TqIQ96fo/NVMVHCvni+gGwEqaYgQxM2GPYGNpXeFfZaGTMFKZQ2Zo
eE/wAHD7Jp0r+OCmqMRxhiTfcriw2Fbwv6kjjyE6/zZM0/4XjxWrpN7DcpZumhnC
l5+m2x8MlfVrtGfZDbMnoKMfuMrVp2HH6yfgE2nzAWfAhjabXxmRLtHDJO21LCM4
tbFZnzUsm51ZZZjF59uN
=Uq2G
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-09 16:20         ` James Hogan
@ 2012-11-09 21:55           ` Arnd Bergmann
  2012-11-09 23:28             ` James Hogan
  2012-11-12 16:59           ` James Hogan
  1 sibling, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-09 21:55 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Friday 09 November 2012, James Hogan wrote:
> On 09/11/12 15:58, Arnd Bergmann wrote:
> > On Friday 09 November 2012, James Hogan wrote:
> >> On 09/11/12 15:06, Arnd Bergmann wrote:
> >>>> +#define DM3_BASE (LINSYSDIRECT_BASE + (MMCU_DIRECTMAPn_ADDR_SCALE * 3))
> >>>
> >>> Again, don't hardcode MMIO addresses like this, but instead get them
> >>> from the device tree to make the code more portable to future systems.
> >>
> >> Even if the addresses are part of the core architecture? I guess DT
> >> could provide the blocks of registers, but it seems like unnecessary
> >> overhead for something that will never change.
> > 
> > The physical addresses might be part of the architecture, but it
> > looks like you are hardcoding the virtual address here, which is
> > the result of an ioremap operation or something similar (I have
> > not looked at this example). It's probably fine to put it into
> > fixmap if that provides a real advantage.
> 
> Ah right, I see what you're getting at. Meta's virtual address space is
> a little non-conventional, which I think makes our approach valid:
> 
> 0x00000000-0x07ffffff and 0x80000000-0x87ffffff
> non-mmuable, always directly maps to physical addresses. These contains
> core registers, the io bus where SoC peripherals tend to go (so for
> generic peripherals in this region we do use ioremap), and core memories
> (same latency as L1 cache).
> 
> 0x08000000-0x7fffffff and 0x88000000-0xffffffff
> mmuable memory (but can't map the non-mmuable region)

Ah, interesting design. Do the two address ranges refer to the same
registers, or are these two distinct areas?

> Therefore our ioremap() returns the input address for the non-mmuable
> regions, and creates an MMU mapping otherwise. So we could ioremap these
> addresses, but for such metag specific code it's faster and less
> complicated not to bother.
> 
> Unfortunately this is also part of the reason we have a prevalence of
> readl/writel warnings when we remove the cast in io.h.

On ARM, we have a (purely software) way to create static mappings using
huge pages for some of the I/O areas, which in effect comes down to
something similar to what you have: If some driver calls ioremap,
we just compute the right virtual address but don't install any new
page table entries for any devices that were already mapped at boot
time.

For any devices that may be used on other architectures, I would
ask you to do the same and still call ioremap in the device driver.

For stuff that is purely meta specifc and also mapped to a fixed location,
it may be better to define your own I/O accessors, such as
meta_in32()/meta_out32 that follow the semantics and calling
conventions you need, rather than mixing them with the regular
readl/writel accessors, even if they share a common implementation.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-09 21:55           ` Arnd Bergmann
@ 2012-11-09 23:28             ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-09 23:28 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 21:55, Arnd Bergmann wrote:
> On Friday 09 November 2012, James Hogan wrote:
>> On 09/11/12 15:58, Arnd Bergmann wrote:
>>> On Friday 09 November 2012, James Hogan wrote:
>>>> On 09/11/12 15:06, Arnd Bergmann wrote:
>>>>>> +#define DM3_BASE (LINSYSDIRECT_BASE + (MMCU_DIRECTMAPn_ADDR_SCALE * 3))
>>>>>
>>>>> Again, don't hardcode MMIO addresses like this, but instead get them
>>>>> from the device tree to make the code more portable to future systems.
>>>>
>>>> Even if the addresses are part of the core architecture? I guess DT
>>>> could provide the blocks of registers, but it seems like unnecessary
>>>> overhead for something that will never change.
>>>
>>> The physical addresses might be part of the architecture, but it
>>> looks like you are hardcoding the virtual address here, which is
>>> the result of an ioremap operation or something similar (I have
>>> not looked at this example). It's probably fine to put it into
>>> fixmap if that provides a real advantage.
>>
>> Ah right, I see what you're getting at. Meta's virtual address space is
>> a little non-conventional, which I think makes our approach valid:
>>
>> 0x00000000-0x07ffffff and 0x80000000-0x87ffffff
>> non-mmuable, always directly maps to physical addresses. These contains
>> core registers, the io bus where SoC peripherals tend to go (so for
>> generic peripherals in this region we do use ioremap), and core memories
>> (same latency as L1 cache).
>>
>> 0x08000000-0x7fffffff and 0x88000000-0xffffffff
>> mmuable memory (but can't map the non-mmuable region)
> 
> Ah, interesting design. Do the two address ranges refer to the same
> registers, or are these two distinct areas?

No, the lower ones are the core/SoC registers, and the upper ones
contain core code and data memories (3 cycle latency, around 64KB of
each usually), and on some SoCs the boot ROM is included in there too.

> 
>> Therefore our ioremap() returns the input address for the non-mmuable
>> regions, and creates an MMU mapping otherwise. So we could ioremap these
>> addresses, but for such metag specific code it's faster and less
>> complicated not to bother.
>>
>> Unfortunately this is also part of the reason we have a prevalence of
>> readl/writel warnings when we remove the cast in io.h.
> 
> On ARM, we have a (purely software) way to create static mappings using
> huge pages for some of the I/O areas, which in effect comes down to
> something similar to what you have: If some driver calls ioremap,
> we just compute the right virtual address but don't install any new
> page table entries for any devices that were already mapped at boot
> time.

Interesting, sounds sort of similar.

> For any devices that may be used on other architectures, I would
> ask you to do the same and still call ioremap in the device driver.

Agreed.

> For stuff that is purely meta specifc and also mapped to a fixed location,
> it may be better to define your own I/O accessors, such as
> meta_in32()/meta_out32 that follow the semantics and calling
> conventions you need, rather than mixing them with the regular
> readl/writel accessors, even if they share a common implementation.

Yeh, that sounds like a good idea. Thanks.

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-09 16:20         ` James Hogan
  2012-11-09 21:55           ` Arnd Bergmann
@ 2012-11-12 16:59           ` James Hogan
  2012-11-12 19:10             ` Arnd Bergmann
  1 sibling, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-12 16:59 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 16:20, James Hogan wrote:
> On 09/11/12 15:58, Arnd Bergmann wrote:
>> On Friday 09 November 2012, James Hogan wrote:
>>> On 09/11/12 15:06, Arnd Bergmann wrote:
>>>> On Wednesday 31 October 2012, James Hogan wrote:
>>
>>>>> + * Despite being a 32bit architecture, Meta can do 64bit memory accesses
>>>>> + * (assuming the bus supports it).
>>>>> + */
>>>>> +
>>>>> +static inline u64 __raw_readq(const volatile void __iomem *addr)
>>>>> +{
>>>>> +	return *(const volatile u64 __force *) addr;
>>>>> +}
>>>>> +#define readq(addr) __raw_readq(addr)
>>>>> +
>>>>> +static inline void __raw_writeq(u64 b, volatile void __iomem *addr)
>>>>> +{
>>>>> +	*(volatile u64 __force *) addr = b;
>>>>> +}
>>>>> +#define writeq(b, addr) __raw_writeq(b, addr)
>>>>
>>>> These should be using an inline assembly to guarantee that it gets
>>>> turned into an atomic access, at least of the architecture has
>>>> an atomic 64-bit load/store from memory operation.
>>>
>>> Is there a particular case you have in mind where the compiler could be
>>> expected not to generate an atomic memory op (I presume you mean e.g. 2
>>> 32bit memory ops)? These are implemented the same as asm-generic/io.h
>>> which only omits these ones because metag is 32bit.
>>>
>>> tbh I don't think the 64 bit accesses are ever actually used, so we
>>> could probably drop these ones.
>>
>> There is nothing forcing a compiler to turn a dereferece of a volatile
>> pointer into a single load or store. We've had issues in the past
>> where a such an access got turned into a series of byte accesses
>> when an mmio data structure gets marked as '__packed', but it could
>> happen in other occasions as well.
> 
> Okay fair enough. We'll use inline asm here instead.

Using inline assembly actually causes us a few problems. There is no way
for inline assembly to allow the compiler to intelligently generate
[Reg+Reg] or [Reg+#imm] memory addressing like it can with volatile (and
even if it could, the constraints would be hard/impossible to express
due to the different encodings of GET and SET that have different
immediate lengths and restrictions on register units). This results in
extra code to do an ADD prior to the memory access, and will also
increase register pressure.

Note that Meta doesn't support unaligned accesses anyway so use of
__packed on MMIO is likely broken from the outset. I'd like to therefore
keep using the <asm-generic/io.h> implementations of the __raw_* functions.

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-12 16:59           ` James Hogan
@ 2012-11-12 19:10             ` Arnd Bergmann
  2012-11-13  9:43               ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-12 19:10 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Monday 12 November 2012, James Hogan wrote:
> Using inline assembly actually causes us a few problems. There is no way
> for inline assembly to allow the compiler to intelligently generate
> [Reg+Reg] or [Reg+#imm] memory addressing like it can with volatile (and
> even if it could, the constraints would be hard/impossible to express
> due to the different encodings of GET and SET that have different
> immediate lengths and restrictions on register units). This results in
> extra code to do an ADD prior to the memory access, and will also
> increase register pressure.

Probably not something to do right away, but you should consider extending
your gcc so you can express the more complex constraints. That will likely
also improve the normal code that gcc generates.

> Note that Meta doesn't support unaligned accesses anyway so use of
> __packed on MMIO is likely broken from the outset.

The problem is actually the opposite: Some people incorrectly annotate
structures that are always aligned to be __packed. This has no effect
on architectures that support unaligned accesses, but causes the
access to be split into byte sized loads and stores on architectures
like Meta that cannot.

Note also that gcc is free to split any accesses for whatever reason
it deems necessary.

> I'd like to therefore
> keep using the <asm-generic/io.h> implementations of the __raw_* functions.

Ok, just be aware that if something breaks in device drivers because of
this, it can be a problem that is very hard to debug.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-12 19:10             ` Arnd Bergmann
@ 2012-11-13  9:43               ` James Hogan
  2012-11-13  9:55                 ` Arnd Bergmann
  0 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-13  9:43 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 12/11/12 19:10, Arnd Bergmann wrote:
> On Monday 12 November 2012, James Hogan wrote:
>> Note that Meta doesn't support unaligned accesses anyway so use of
>> __packed on MMIO is likely broken from the outset.
> 
> The problem is actually the opposite: Some people incorrectly annotate
> structures that are always aligned to be __packed. This has no effect
> on architectures that support unaligned accesses, but causes the
> access to be split into byte sized loads and stores on architectures
> like Meta that cannot.
> 
> Note also that gcc is free to split any accesses for whatever reason
> it deems necessary.

Hmm, I found the thread "ARM unaligned MMIO access with
attribute((packed))" which appears to be one of the problems you're
referring to. With some experimentation I can't reproduce this kind of
problem with our current compiler, but it's certainly cause for concern.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 00/40] Meta Linux Kernel Port
  2012-11-13  9:43               ` James Hogan
@ 2012-11-13  9:55                 ` Arnd Bergmann
  0 siblings, 0 replies; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-13  9:55 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Tuesday 13 November 2012, James Hogan wrote:
> Hmm, I found the thread "ARM unaligned MMIO access with
> attribute((packed))" which appears to be one of the problems you're
> referring to. With some experimentation I can't reproduce this kind of
> problem with our current compiler, but it's certainly cause for concern.

Right. The main problem for reproducing this is that it depends on
undefined behavior in C (casting between pointers to types of different
alignment), and what gcc does in this case has changed a couple of times.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 32/40] i8042: don't build on METAG
  2012-11-09 14:28   ` Arnd Bergmann
@ 2012-11-16 11:06     ` James Hogan
  2012-11-16 15:20       ` Arnd Bergmann
  0 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-16 11:06 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 14:28, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
>> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
>> index 55f2c22..60b2a09 100644
>> --- a/drivers/input/serio/Kconfig
>> +++ b/drivers/input/serio/Kconfig
>> @@ -22,7 +22,7 @@ config SERIO_I8042
>>         tristate "i8042 PC Keyboard controller" if EXPERT || !X86
>>         default y
>>         depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
>> -                  (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
>> +                  (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !METAG
>>         help
>>           i8042 is the chip over which the standard AT keyboard and PS/2
>>           mouse are connected to the computer. If you use these devices,
> 
> Maybe you can turn that list around and introduce a new symbol
> ARCH_HAS_SERIO_I8042 or similar that architectures can select.
> I suspect that actually most don't have this, but they have not
> bothered to update this list.

Hi Arnd,

I've starting writing some patches along these lines (for the various PC
style devices). They all seem to have slightly different rules though so
I think to be safe we need separate Kconfig symbols for each one.

With SERIO_I8042 I'm completely guessing. It defaults to y, but the only
ones I know for certain shouldn't select it are the ones currently
excluded (parisc, arm (unless shark, footbridge), superh (unless
cayman), m68k, blackfin, metag). A few I know should allow it due to
special casing of io operations (mips (jazz, sgi_has_i8042, sni_rm),
ppc, sparc, x86, ia64, unicore32). Is it okay in this case to select it
for pretty much all of the arches not excluded and let the maintainers
NAK if they don't want it?

PARPORT_PC isn't too difficult (and defaults to n), I can select
HAVE_PARPORT_PC from any arch that provides asm/parport.h or
conditionally allowed it before:
alpha, arm, ia64, m68k (if ISA), microblaze, mips, parisc, powerpc, sh,
sparc (if PCI), tile, unicore32, x86

VGA_CONSOLE isn't too bad either (but it does default to y), I can
select HAVE_VGA_CONSOLE if the arch declares a screen_info struct (which
is the link error I get on metag):
alpha, arm (only ARCH_INTEGRATOR, ARCH_FOOTBRIDGE, ARCH_NETWINDER),
cris, ia64, m32r, mips, PPC (!4xx && !8xx), score, x86, xtensa

RTC defaults to n, but I've narrowed it down to alpha, m32r, mips
(MACH_LOONSON), mn10300, and x86 which provide asm/mc146818rtc.h.

GEN_RTC defaults to n, and I've narrowed to alpha, m68k, mn10300,
parisc, ppc, x86 which provide asm/rtc.h.

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 32/40] i8042: don't build on METAG
  2012-11-16 11:06     ` James Hogan
@ 2012-11-16 15:20       ` Arnd Bergmann
  2012-11-16 16:19         ` Geert Uytterhoeven
  2012-11-16 16:50         ` James Hogan
  0 siblings, 2 replies; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-16 15:20 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Friday 16 November 2012, James Hogan wrote:
> On 09/11/12 14:28, Arnd Bergmann wrote:
> > On Wednesday 31 October 2012, James Hogan wrote:
> >> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> >> index 55f2c22..60b2a09 100644
> >> --- a/drivers/input/serio/Kconfig
> >> +++ b/drivers/input/serio/Kconfig
> >> @@ -22,7 +22,7 @@ config SERIO_I8042
> >>         tristate "i8042 PC Keyboard controller" if EXPERT || !X86
> >>         default y
> >>         depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
> >> -                  (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
> >> +                  (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !METAG
> >>         help
> >>           i8042 is the chip over which the standard AT keyboard and PS/2
> >>           mouse are connected to the computer. If you use these devices,
> > 
> > Maybe you can turn that list around and introduce a new symbol
> > ARCH_HAS_SERIO_I8042 or similar that architectures can select.
> > I suspect that actually most don't have this, but they have not
> > bothered to update this list.
> 
> Hi Arnd,
> 
> I've starting writing some patches along these lines (for the various PC
> style devices). They all seem to have slightly different rules though so
> I think to be safe we need separate Kconfig symbols for each one.

First of all, thank you very much for your investigation so far!

I think a lot can be done with one Kconfig symbol, if each of the users
lists the difference from the way the generic one is described, e.g.

config SERIO_I8042
	depends on HAVE_LEGACY_PC_IO && !M68K || ARCH_SHARK || FOOTBRIDGE_HOST

to define one that can be set on anything that generally is PC compatible
with the exception of all M68K but also including some ARM platforms.

> With SERIO_I8042 I'm completely guessing. It defaults to y, but the only
> ones I know for certain shouldn't select it are the ones currently
> excluded (parisc, arm (unless shark, footbridge), superh (unless
> cayman), m68k, blackfin, metag). A few I know should allow it due to
> special casing of io operations (mips (jazz, sgi_has_i8042, sni_rm),
> ppc, sparc, x86, ia64, unicore32). Is it okay in this case to select it
> for pretty much all of the arches not excluded and let the maintainers
> NAK if they don't want it?

Yes, that sounds good. You can even do that patch to change over the other
architectures straight away and let the ones that don't actually want it
send a separate patch. Note that S390 and UML can't select SERIO because
they disable INPUT altogether.

It's probably a good idea to separate the inversion of the logic from
any policy change and put those into separate patches.

> PARPORT_PC isn't too difficult (and defaults to n), I can select
> HAVE_PARPORT_PC from any arch that provides asm/parport.h or
> conditionally allowed it before:
> alpha, arm, ia64, m68k (if ISA), microblaze, mips, parisc, powerpc, sh,
> sparc (if PCI), tile, unicore32, x86

Ok. I think in general, we want to enable this one for anything
that has PCI, because the driver also supports parallel ports
on an extension card, not just those with a hardwired address.

> VGA_CONSOLE isn't too bad either (but it does default to y), I can
> select HAVE_VGA_CONSOLE if the arch declares a screen_info struct (which
> is the link error I get on metag):
> alpha, arm (only ARCH_INTEGRATOR, ARCH_FOOTBRIDGE, ARCH_NETWINDER),
> cris, ia64, m32r, mips, PPC (!4xx && !8xx), score, x86, xtensa

Ok. I don't think we can actually support it on m32r or score, but it
doesn't hurt to enable it.

> RTC defaults to n, but I've narrowed it down to alpha, m32r, mips
> (MACH_LOONSON), mn10300, and x86 which provide asm/mc146818rtc.h.
> 
> GEN_RTC defaults to n, and I've narrowed to alpha, m68k, mn10300,
> parisc, ppc, x86 which provide asm/rtc.h.

It's amazing that those are still around after we've had RTC_LIB
for so long. The "modern" driver that is supposed to replace these
two is RTC_DRV_CMOS, which currently builds for X86 || ALPHA || 
ARM || M32R || ATARI || PPC || MIPS || SPARC64.

There is also the nvram driver that is tightly coupled to the RTC
support and that can build only on x86, m68k (atari) and
ARM (if RTC_DRV_CMOS is on).

I think the baseline should be:

config LEGACY_PC_IO
	bool
	# selected by X86 || ALPHA || SPARC || M32R ||
	# (PPC && !4xx && !8xx) || 
        # (ARM && (ARCH_FOOTBRIDGE || ARCH_NETWINDER) ||
	# (M68K && (Q40 || SUN3X)) ||
	# (MIPS && (JAZZ || MALTA || SNI_RM))

config ARCH_MAY_HAVE_PC_FDC
	def_bool LEGACY_PC_IO && !M32R && (!PPC || (PCI && !PPC_PSERIES))
	# selected by ARCH_RPC

config HAVE_PARPORT_PC
	def_bool LEGACY_PC_IO || PCI

config HAVE_VGA_CONSOLE
	def_bool LEGACY_PC_IO
	# selected by ARCH_INTEGRATOR || CRIS || IA64 || MIPS || SCORE || XTENSA

config HAVE_RTC_DRV_CMOS
	def_bool LEGACY_PC_IO
	# selected by ARM || ATARI || PPC || MIPS || SPARC64

config HAVE_RTC_CMOS_LEGACY
	bool
	# selected by MACH_LOONSON || MN10300 || X86 || ALPHA || MN10300 || M32R

config HAVE_GEN_RTC_LEGACY
	def_bool LEGACY_PC_IO && !(ARM || M32R || SPARC)
	# selected by M68K || MN10300 || PARISC || PPC


	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 32/40] i8042: don't build on METAG
  2012-11-16 15:20       ` Arnd Bergmann
@ 2012-11-16 16:19         ` Geert Uytterhoeven
  2012-11-16 16:43           ` Arnd Bergmann
  2012-11-16 16:50         ` James Hogan
  1 sibling, 1 reply; 116+ messages in thread
From: Geert Uytterhoeven @ 2012-11-16 16:19 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: James Hogan, linux-arch

On Fri, Nov 16, 2012 at 4:20 PM, Arnd Bergmann <arnd@arndb.de> wrote:
>> RTC defaults to n, but I've narrowed it down to alpha, m32r, mips
>> (MACH_LOONSON), mn10300, and x86 which provide asm/mc146818rtc.h.
>>
>> GEN_RTC defaults to n, and I've narrowed to alpha, m68k, mn10300,
>> parisc, ppc, x86 which provide asm/rtc.h.
>
> It's amazing that those are still around after we've had RTC_LIB
> for so long. The "modern" driver that is supposed to replace these
> two is RTC_DRV_CMOS, which currently builds for X86 || ALPHA ||
> ARM || M32R || ATARI || PPC || MIPS || SPARC64.

> config HAVE_GEN_RTC_LEGACY
>         def_bool LEGACY_PC_IO && !(ARM || M32R || SPARC)
>         # selected by M68K || MN10300 || PARISC || PPC

That's not correct: GEN_RTC is not a driver for legacy CMOS RTC, but a
driver that provides a clasic /dev/rtc userspace API for systems that do not
have a CMOS RTC, but do provie [gs]et_rtc_time().
So it's a deprecated complementary driver for RTC.

It's replacement using RTC_CLASS is RTC_DRV_GENERIC, which is also
discouraged for new platforms:

config RTC_DRV_GENERIC
        tristate "Generic RTC support"
        # Please consider writing a new RTC driver instead of using the generic
        # RTC abstraction
        depends on PARISC || M68K || PPC || SUPERH32
        help
          Say Y or M here to enable RTC support on systems using the generic
          RTC abstraction. If you do not know what you are doing, you should
          just say Y.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 32/40] i8042: don't build on METAG
  2012-11-16 16:19         ` Geert Uytterhoeven
@ 2012-11-16 16:43           ` Arnd Bergmann
  0 siblings, 0 replies; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-16 16:43 UTC (permalink / raw)
  To: Geert Uytterhoeven; +Cc: James Hogan, linux-arch

On Friday 16 November 2012, Geert Uytterhoeven wrote:
> > config HAVE_GEN_RTC_LEGACY
> >         def_bool LEGACY_PC_IO && !(ARM || M32R || SPARC)
> >         # selected by M68K || MN10300 || PARISC || PPC
> 
> That's not correct: GEN_RTC is not a driver for legacy CMOS RTC, but a
> driver that provides a clasic /dev/rtc userspace API for systems that do not
> have a CMOS RTC, but do provie [gs]et_rtc_time().

Right, I was confusing it with the other symbol I suggested introducing,
HAVE_RTC_CMOS_LEGACY, which instead should default to LEGACY_PC_IO && !(...).

> So it's a deprecated complementary driver for RTC.
> 
> It's replacement using RTC_CLASS is RTC_DRV_GENERIC, which is also
> discouraged for new platforms:
> 
> config RTC_DRV_GENERIC
>         tristate "Generic RTC support"
>         # Please consider writing a new RTC driver instead of using the generic
>         # RTC abstraction
>         depends on PARISC || M68K || PPC || SUPERH32
>         help
>           Say Y or M here to enable RTC support on systems using the generic
>           RTC abstraction. If you do not know what you are doing, you should
>           just say Y.

Ah, I wasn't aware of this one. We clearly have too many of those ;-)

On a related note, I wonder if it makes sense to move the four non-RTC_LIB
drivers out of drivers/char and into drivers/rtc/legacy/ or something like
that. We wouldn't change the code of course, but in general I like the
idea of grouping code together that does the same thing, even if it doesn't
use the same in-kernel interfaces.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 32/40] i8042: don't build on METAG
  2012-11-16 15:20       ` Arnd Bergmann
  2012-11-16 16:19         ` Geert Uytterhoeven
@ 2012-11-16 16:50         ` James Hogan
  2012-11-16 17:20           ` Arnd Bergmann
  1 sibling, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-16 16:50 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

Hi Arnd,

On 16/11/12 15:20, Arnd Bergmann wrote:
> I think the baseline should be:
> 
> config LEGACY_PC_IO
> 	bool
> 	# selected by X86 || ALPHA || SPARC || M32R ||
> 	# (PPC && !4xx && !8xx) || 
>         # (ARM && (ARCH_FOOTBRIDGE || ARCH_NETWINDER) ||
> 	# (M68K && (Q40 || SUN3X)) ||
> 	# (MIPS && (JAZZ || MALTA || SNI_RM))

Thanks!

> 
> config ARCH_MAY_HAVE_PC_FDC
> 	def_bool LEGACY_PC_IO && !M32R && (!PPC || (PCI && !PPC_PSERIES))
> 	# selected by ARCH_RPC
> 
> config HAVE_PARPORT_PC
> 	def_bool LEGACY_PC_IO || PCI

So would you say that all architecture's that can have PCI should have a
parport.h (generic-y at least)? Current architectures which can enable
non-broken PCI and don't have parport.h are:
currently not excluded: CRIS
currently excluded: FRV, MN10300, XTENSA

Cheers
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 32/40] i8042: don't build on METAG
  2012-11-16 16:50         ` James Hogan
@ 2012-11-16 17:20           ` Arnd Bergmann
  0 siblings, 0 replies; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-16 17:20 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Friday 16 November 2012, James Hogan wrote:
> > 
> > config ARCH_MAY_HAVE_PC_FDC
> > 	def_bool LEGACY_PC_IO && !M32R && (!PPC || (PCI && !PPC_PSERIES))
> > 	# selected by ARCH_RPC
> > 
> > config HAVE_PARPORT_PC
> > 	def_bool LEGACY_PC_IO || PCI
> 
> So would you say that all architecture's that can have PCI should have a
> parport.h (generic-y at least)? Current architectures which can enable
> non-broken PCI and don't have parport.h are:
> currently not excluded: CRIS
> currently excluded: FRV, MN10300, XTENSA

I think they should just all generate a link to asm-generic/parport.h, which
would do the right thing for them.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-11-09 14:46   ` Arnd Bergmann
  2012-11-09 14:55     ` James Hogan
@ 2012-11-20 15:06     ` James Hogan
  2012-11-20 15:43       ` Arnd Bergmann
  1 sibling, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-20 15:06 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 14:46, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
>> +
>> +define archhelp
>> +  echo  '* vmlinux.bin	- Binary kernel image (arch/$(ARCH)/boot/vmlinux.bin)'
>> +  @echo '  uImage  	- Alias to bootable U-Boot image'
>> +  @echo '  uImage.bin	- Kernel-only image for U-Boot (bin)'
>> +  @echo '  uImage.gz	- Kernel-only image for U-Boot (gzip)'
>> +  @echo '  uImage.bz2	- Kernel-only image for U-Boot (bzip2)'
>> +  @echo '  uImage.lzma	- Kernel-only image for U-Boot (lzma)'
>> +  @echo '  uImage.xz	- Kernel-only image for U-Boot (xz)'
>> +  @echo '  uImage.lzo	- Kernel-only image for U-Boot (lzo)'
>> +endef
> 
> The uImage format is causing some trouble on ARM because it requires knowing
> the load address at boot time, so we try to move people towards using just
> zImage files there. You might want to consider the same.

Could you point me at some info about this problem? I'm not clear why
needing to know the load address at boot is a problem, or how zImage
helps. Does this presume that the bootloader starts the kernel with the
MMU bypassed?

> It's probably not necessary to have both lzma and xz formats since they
> are the same algorithm with different headers afaik.

This was copied from arch/sh/boot/Makefile if I remember right. Is
either of them preferred?

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-11-20 15:06     ` James Hogan
@ 2012-11-20 15:43       ` Arnd Bergmann
  2012-11-20 16:01         ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-20 15:43 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Tuesday 20 November 2012, James Hogan wrote:
> On 09/11/12 14:46, Arnd Bergmann wrote:
> > On Wednesday 31 October 2012, James Hogan wrote:
> >> +
> >> +define archhelp
> >> +  echo  '* vmlinux.bin      - Binary kernel image (arch/$(ARCH)/boot/vmlinux.bin)'
> >> +  @echo '  uImage   - Alias to bootable U-Boot image'
> >> +  @echo '  uImage.bin       - Kernel-only image for U-Boot (bin)'
> >> +  @echo '  uImage.gz        - Kernel-only image for U-Boot (gzip)'
> >> +  @echo '  uImage.bz2       - Kernel-only image for U-Boot (bzip2)'
> >> +  @echo '  uImage.lzma      - Kernel-only image for U-Boot (lzma)'
> >> +  @echo '  uImage.xz        - Kernel-only image for U-Boot (xz)'
> >> +  @echo '  uImage.lzo       - Kernel-only image for U-Boot (lzo)'
> >> +endef
> > 
> > The uImage format is causing some trouble on ARM because it requires knowing
> > the load address at boot time, so we try to move people towards using just
> > zImage files there. You might want to consider the same.
> 
> Could you point me at some info about this problem? I'm not clear why
> needing to know the load address at boot is a problem, or how zImage
> helps. Does this presume that the bootloader starts the kernel with the
> MMU bypassed?

A lot of architectures don't have a fixed location for the start of
physical RAM, so the arguments to u-boot mkimage are configuration
dependent. If you want to build a kernel that runs on multiple
systems with a conflicting requirements, you can't put the load address
into a u-boot header.

> > It's probably not necessary to have both lzma and xz formats since they
> > are the same algorithm with different headers afaik.
> 
> This was copied from arch/sh/boot/Makefile if I remember right. Is
> either of them preferred?

xz is the one that most people use today.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 12/40] metag: Build infrastructure
  2012-11-20 15:43       ` Arnd Bergmann
@ 2012-11-20 16:01         ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-20 16:01 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 20/11/12 15:43, Arnd Bergmann wrote:
> On Tuesday 20 November 2012, James Hogan wrote:
>> On 09/11/12 14:46, Arnd Bergmann wrote:
>>> On Wednesday 31 October 2012, James Hogan wrote:
>>>> +
>>>> +define archhelp
>>>> +  echo  '* vmlinux.bin      - Binary kernel image (arch/$(ARCH)/boot/vmlinux.bin)'
>>>> +  @echo '  uImage   - Alias to bootable U-Boot image'
>>>> +  @echo '  uImage.bin       - Kernel-only image for U-Boot (bin)'
>>>> +  @echo '  uImage.gz        - Kernel-only image for U-Boot (gzip)'
>>>> +  @echo '  uImage.bz2       - Kernel-only image for U-Boot (bzip2)'
>>>> +  @echo '  uImage.lzma      - Kernel-only image for U-Boot (lzma)'
>>>> +  @echo '  uImage.xz        - Kernel-only image for U-Boot (xz)'
>>>> +  @echo '  uImage.lzo       - Kernel-only image for U-Boot (lzo)'
>>>> +endef
>>>
>>> The uImage format is causing some trouble on ARM because it requires knowing
>>> the load address at boot time, so we try to move people towards using just
>>> zImage files there. You might want to consider the same.
>>
>> Could you point me at some info about this problem? I'm not clear why
>> needing to know the load address at boot is a problem, or how zImage
>> helps. Does this presume that the bootloader starts the kernel with the
>> MMU bypassed?
> 
> A lot of architectures don't have a fixed location for the start of
> physical RAM, so the arguments to u-boot mkimage are configuration
> dependent. If you want to build a kernel that runs on multiple
> systems with a conflicting requirements, you can't put the load address
> into a u-boot header.

Right okay. We have a similar situation for physical RAM addresses, but
at the moment the MMU is turned on before the main bootloader, so it's
always loaded at the same address the kernel runs at. I'll certainly
bear that in mind though if we move MMU init into the kernel.

>>> It's probably not necessary to have both lzma and xz formats since they
>>> are the same algorithm with different headers afaik.
>>
>> This was copied from arch/sh/boot/Makefile if I remember right. Is
>> either of them preferred?
> 
> xz is the one that most people use today.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 17/40] metag: IRQ handling
  2012-11-09 14:12   ` Arnd Bergmann
@ 2012-11-20 16:08     ` James Hogan
  2012-11-20 16:15       ` Arnd Bergmann
  0 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-11-20 16:08 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 14:12, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
> 
>> diff --git a/arch/metag/include/asm/irq.h b/arch/metag/include/asm/irq.h
>> new file mode 100644
>> index 0000000..da24886
>> --- /dev/null
>> +++ b/arch/metag/include/asm/irq.h
>> +#ifndef HW_IRQS
>> +#define HW_IRQS  (HWSTATMETA_OFFSET_MAX + HWSTATEXT_OFFSET_MAX + \
>> +		HWSTATEXT2_OFFSET_MAX + HWSTATEXT4_OFFSET_MAX + \
>> +		HWSTATEXT6_OFFSET_MAX)
>> +#endif
>> +
>> +#define META_IRQS 32
>> +
>> +#define NR_IRQS (META_IRQS + HW_IRQS)
> 
> I think you should use sparse IRQs right from the start and avoid
> hardcoding interrupt numbers from the start.

I presume core irq numbers need hard coding (e.g. those required by core
code like cross-hardware-thread irqs and perf counters)? Or would the
recommended way be to have a function which is given the core hw irq
number and converts/maps a virtual irq for it using the irq domain?

>> +/**
>> + * meta_intc_stat_addr() - Get the address of a HWSTATEXT register
>> + * @hw:		Hardware IRQ number (within external trigger block)
>> + *
>> + * Returns:	Address of a HWSTATEXT register containing the status bit for
>> + *		the specified hardware IRQ number
>> + */
>> +static void __iomem *meta_intc_stat_addr(irq_hw_number_t hw)
>> +{
>> +	return (void __iomem *)(HWSTATEXT +
>> +				HWSTAT_STRIDE * meta_intc_bank(hw));
>> +}
> 
> Hardcoding register addresses like this is considered bad style, even for
> an interrupt controller. Better use ioremap to initialize a pointer
> dynamically at boot time.

All these registers are part of the core Meta registers in the non-MMU
region and so are always fixed. I have altered this file to use
metag_in32/metag_out32 instead of readl/writel to make that clearer.

>> +struct irq_chip meta_intc_edge_chip = {
>> +	.irq_startup		= meta_intc_startup_irq,
>> +	.irq_shutdown		= meta_intc_shutdown_irq,
>> +	.irq_ack		= meta_intc_ack_irq,
>> +	.irq_mask		= meta_intc_mask_irq,
>> +	.irq_unmask		= meta_intc_unmask_irq,
>> +	.irq_set_type		= meta_intc_irq_set_type,
>> +	.irq_set_affinity	= meta_intc_set_affinity,
>> +	.flags			= META_INTC_CHIP_FLAGS,
>> +};
>> +
>> +struct irq_chip meta_intc_level_chip = {
>> +	.irq_startup		= meta_intc_startup_irq,
>> +	.irq_shutdown		= meta_intc_shutdown_irq,
>> +	.irq_set_type		= meta_intc_irq_set_type,
>> +	.irq_mask		= meta_intc_mask_irq,
>> +	.irq_unmask		= meta_intc_unmask_irq,
>> +	.irq_set_affinity	= meta_intc_set_affinity,
>> +	.flags			= META_INTC_CHIP_FLAGS,
>> +};
> 
> I would recommend trying to put the external irq_chip code into
> drivers/irqchip, which we are currently establishing. Right now,
> that directory is still a bit dependent on the ARM architecture,
> but IMHO that is an even stronger reason to use it for a new
> architecture, so we can make it arch independent in the process,
> without the fear of breaking existing users. After it works
> on multiple architectures, we can start migrating the others.

I'll look into this.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 17/40] metag: IRQ handling
  2012-11-20 16:08     ` James Hogan
@ 2012-11-20 16:15       ` Arnd Bergmann
  2012-12-18 13:25         ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-20 16:15 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Tuesday 20 November 2012, James Hogan wrote:
> On 09/11/12 14:12, Arnd Bergmann wrote:
> > On Wednesday 31 October 2012, James Hogan wrote:
> > 
> >> diff --git a/arch/metag/include/asm/irq.h b/arch/metag/include/asm/irq.h
> >> new file mode 100644
> >> index 0000000..da24886
> >> --- /dev/null
> >> +++ b/arch/metag/include/asm/irq.h
> >> +#ifndef HW_IRQS
> >> +#define HW_IRQS  (HWSTATMETA_OFFSET_MAX + HWSTATEXT_OFFSET_MAX + \
> >> +		HWSTATEXT2_OFFSET_MAX + HWSTATEXT4_OFFSET_MAX + \
> >> +		HWSTATEXT6_OFFSET_MAX)
> >> +#endif
> >> +
> >> +#define META_IRQS 32
> >> +
> >> +#define NR_IRQS (META_IRQS + HW_IRQS)
> > 
> > I think you should use sparse IRQs right from the start and avoid
> > hardcoding interrupt numbers from the start.
> 
> I presume core irq numbers need hard coding (e.g. those required by core
> code like cross-hardware-thread irqs and perf counters)? Or would the
> recommended way be to have a function which is given the core hw irq
> number and converts/maps a virtual irq for it using the irq domain?

Anything that is probed as a platform device can get the interrupt numbers
using the device resources that are filled in by the device tree probe.

If you need something earlier, you can call irq_of_parse_and_map()
manually.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 18/40] metag: System Calls
  2012-11-09 14:20   ` Arnd Bergmann
@ 2012-11-22 12:01     ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-22 12:01 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

Hi Arnd,

On 09/11/12 14:20, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
> 
>> +/* kernel/signal.c */
>> +asmlinkage int sys_sigaltstack(const stack_t __user *, stack_t __user *,
>> +			       unsigned long, unsigned long,
>> +			       unsigned long, unsigned long,
>> +			       struct pt_regs *);
>> +asmlinkage int sys_rt_sigreturn(unsigned long, unsigned long,
>> +				unsigned long, unsigned long,
>> +				unsigned long, unsigned long,
>> +				struct pt_regs *);
>> +asmlinkage long sys_rt_sigaction(int sig,
>> +				 const struct sigaction __user *act,
>> +				 struct sigaction __user *oact,
>> +				 size_t sigsetsize);
>> +asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset,
>> +				  size_t sigsetsize);
>> +asmlinkage long sys_rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t __user *);
> 
> Can't you use the declarations from asm-generic/syscalls.h for these?

Thanks. I'll use that.

>>> +asmlinkage long sys32_truncate64(const char __user *, unsigned long,
>> +				 unsigned long);
>> +asmlinkage long sys32_ftruncate64(unsigned int, unsigned long,
>> +				  unsigned long);
>> +asmlinkage long sys32_fadvise64_64(int, unsigned long, unsigned long,
>> +				   unsigned long, unsigned long, int);
>> +asmlinkage long sys32_readahead(int, unsigned long, unsigned long, size_t);
>> +asmlinkage ssize_t sys32_pread64(unsigned long, char __user *, size_t,
>> +				 unsigned long, unsigned long);
>> +asmlinkage ssize_t sys32_pwrite64(unsigned long, char __user *, size_t,
>> +				  unsigned long, unsigned long);
>> +asmlinkage long sys32_sync_file_range(int, unsigned long, unsigned long,
>> +				      unsigned long, unsigned long,
>> +				      unsigned int);
> 
> These confused me a bit. The sys32_* notation is traditionally used for
> 32 bit compatibility functions on 64 bit architectures. Maybe it's better
> to name these sys_meta_* instead.

Okay, I'll probably go with sys_*64_metag to match c6x.

> 
>> +asmlinkage int sys_metag_spinlock(int __user *spinlock)
>> +{
>> +	int ret = 0, tmp;
>> +
>> +	local_irq_disable();
>> +	get_user(tmp, spinlock);
>> +	if (tmp)
>> +		ret = 1;
>> +	tmp = 1;
>> +	put_user(tmp, spinlock);
>> +	local_irq_enable();
>> +	return ret;
>> +}
> 
> This seems to be a specialization of a cmpxchg() function. How about
> implementing cmpxchg semantics instead? It should be more flexible
> for almost the same runtime cost.

This is a bit of a legacy syscall, and as of very recently it is no
longer used by up to date user software. The gateway page implements
cmpxchg semantics, so I'll drop this system call altogether.

Thanks for the comments
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
  2012-11-09 14:25   ` Arnd Bergmann
@ 2012-11-23 15:53     ` James Hogan
  2012-11-23 16:20         ` James Hogan
  2012-11-23 16:47       ` Arnd Bergmann
  0 siblings, 2 replies; 116+ messages in thread
From: James Hogan @ 2012-11-23 15:53 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch

On 09/11/12 14:25, Arnd Bergmann wrote:
> On Wednesday 31 October 2012, James Hogan wrote:
>> +static inline void
>> +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
>> +                enum dma_data_direction direction)
>> +{
>> +       BUG_ON(!valid_dma_direction(direction));
>> +       dma_sync_for_cpu((void *)bus_to_virt(dma_addr), size, direction);
>> +}
> 
> bus_to_virt is deprecated an should not be visible to device drivers
> any more for new stuff. Maybe you can remove the definition and introduce
> a __bus_to_virt() function for internal use that also returns a pointer
> type instead.

Hi Arnd,

Is there any reason not to just directly use phys_to_virt (which
bus_to_virt was defined as before)?

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
  2012-11-23 15:53     ` James Hogan
@ 2012-11-23 16:20         ` James Hogan
  2012-11-23 16:47       ` Arnd Bergmann
  1 sibling, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-23 16:20 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch, linux-kernel

On 23/11/12 15:53, James Hogan wrote:
> On 09/11/12 14:25, Arnd Bergmann wrote:
>> On Wednesday 31 October 2012, James Hogan wrote:
>>> +static inline void
>>> +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
>>> +                enum dma_data_direction direction)
>>> +{
>>> +       BUG_ON(!valid_dma_direction(direction));
>>> +       dma_sync_for_cpu((void *)bus_to_virt(dma_addr), size, direction);
>>> +}
>>
>> bus_to_virt is deprecated an should not be visible to device drivers
>> any more for new stuff. Maybe you can remove the definition and introduce
>> a __bus_to_virt() function for internal use that also returns a pointer
>> type instead.
> 
> Hi Arnd,
> 
> Is there any reason not to just directly use phys_to_virt (which
> bus_to_virt was defined as before)?
> 
> Thanks
> James
> 

asm-generic/io.h has bitten me. Does the following look reasonable?

Thanks
James

Subject: [PATCH 1/1] asm-generic/io.h: check CONFIG_VIRT_TO_BUS

Make asm-generic/io.h check CONFIG_VIRT_TO_BUS before defining
virt_to_bus() and bus_to_virt(), otherwise it's easy to accidentally
have a silently failing incorrect direct mapped definition rather then
no definition at all.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 include/asm-generic/io.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 616eea5..34823ef 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -350,6 +350,7 @@ extern void ioport_unmap(void __iomem *p);
 #define xlate_dev_kmem_ptr(p)	p
 #define xlate_dev_mem_ptr(p)	__va(p)
 
+#ifdef CONFIG_VIRT_TO_BUS
 #ifndef virt_to_bus
 static inline unsigned long virt_to_bus(volatile void *address)
 {
@@ -361,6 +362,7 @@ static inline void *bus_to_virt(unsigned long address)
 	return (void *) address;
 }
 #endif
+#endif
 
 #define memset_io(a, b, c)	memset(__io_virt(a), (b), (c))
 #define memcpy_fromio(a, b, c)	memcpy((a), __io_virt(b), (c))
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
@ 2012-11-23 16:20         ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-11-23 16:20 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch, linux-kernel

On 23/11/12 15:53, James Hogan wrote:
> On 09/11/12 14:25, Arnd Bergmann wrote:
>> On Wednesday 31 October 2012, James Hogan wrote:
>>> +static inline void
>>> +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
>>> +                enum dma_data_direction direction)
>>> +{
>>> +       BUG_ON(!valid_dma_direction(direction));
>>> +       dma_sync_for_cpu((void *)bus_to_virt(dma_addr), size, direction);
>>> +}
>>
>> bus_to_virt is deprecated an should not be visible to device drivers
>> any more for new stuff. Maybe you can remove the definition and introduce
>> a __bus_to_virt() function for internal use that also returns a pointer
>> type instead.
> 
> Hi Arnd,
> 
> Is there any reason not to just directly use phys_to_virt (which
> bus_to_virt was defined as before)?
> 
> Thanks
> James
> 

asm-generic/io.h has bitten me. Does the following look reasonable?

Thanks
James

Subject: [PATCH 1/1] asm-generic/io.h: check CONFIG_VIRT_TO_BUS

Make asm-generic/io.h check CONFIG_VIRT_TO_BUS before defining
virt_to_bus() and bus_to_virt(), otherwise it's easy to accidentally
have a silently failing incorrect direct mapped definition rather then
no definition at all.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
 include/asm-generic/io.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 616eea5..34823ef 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -350,6 +350,7 @@ extern void ioport_unmap(void __iomem *p);
 #define xlate_dev_kmem_ptr(p)	p
 #define xlate_dev_mem_ptr(p)	__va(p)
 
+#ifdef CONFIG_VIRT_TO_BUS
 #ifndef virt_to_bus
 static inline unsigned long virt_to_bus(volatile void *address)
 {
@@ -361,6 +362,7 @@ static inline void *bus_to_virt(unsigned long address)
 	return (void *) address;
 }
 #endif
+#endif
 
 #define memset_io(a, b, c)	memset(__io_virt(a), (b), (c))
 #define memcpy_fromio(a, b, c)	memcpy((a), __io_virt(b), (c))
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
  2012-11-23 15:53     ` James Hogan
  2012-11-23 16:20         ` James Hogan
@ 2012-11-23 16:47       ` Arnd Bergmann
  1 sibling, 0 replies; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-23 16:47 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch

On Friday 23 November 2012, James Hogan wrote:
> 
> On 09/11/12 14:25, Arnd Bergmann wrote:
> > On Wednesday 31 October 2012, James Hogan wrote:
> >> +static inline void
> >> +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
> >> +                enum dma_data_direction direction)
> >> +{
> >> +       BUG_ON(!valid_dma_direction(direction));
> >> +       dma_sync_for_cpu((void *)bus_to_virt(dma_addr), size, direction);
> >> +}
> > 
> > bus_to_virt is deprecated an should not be visible to device drivers
> > any more for new stuff. Maybe you can remove the definition and introduce
> > a __bus_to_virt() function for internal use that also returns a pointer
> > type instead.
> 
> Is there any reason not to just directly use phys_to_virt (which
> bus_to_virt was defined as before)?

That should be fine in this case. It's just that device drivers must
not rely directly on a particular mapping between bus and virt addresses.
dma_map_single is are the right place to put this mapping.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
  2012-11-23 16:20         ` James Hogan
  (?)
@ 2012-11-23 16:47         ` Arnd Bergmann
  2013-01-09 16:04             ` James Hogan
  -1 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-11-23 16:47 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, linux-kernel

On Friday 23 November 2012, James Hogan wrote:
> Subject: [PATCH 1/1] asm-generic/io.h: check CONFIG_VIRT_TO_BUS
> 
> Make asm-generic/io.h check CONFIG_VIRT_TO_BUS before defining
> virt_to_bus() and bus_to_virt(), otherwise it's easy to accidentally
> have a silently failing incorrect direct mapped definition rather then
> no definition at all.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>

Good catch!

Acked-by: Arnd Bergmann <arnd@arndb.de>

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 17/40] metag: IRQ handling
  2012-11-20 16:15       ` Arnd Bergmann
@ 2012-12-18 13:25         ` James Hogan
  2012-12-19 15:58           ` Arnd Bergmann
  0 siblings, 1 reply; 116+ messages in thread
From: James Hogan @ 2012-12-18 13:25 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch, Grant

On 20/11/12 16:15, Arnd Bergmann wrote:
> On Tuesday 20 November 2012, James Hogan wrote:
>> On 09/11/12 14:12, Arnd Bergmann wrote:
>>> On Wednesday 31 October 2012, James Hogan wrote:
>>>
>>>> diff --git a/arch/metag/include/asm/irq.h b/arch/metag/include/asm/irq.h
>>>> new file mode 100644
>>>> index 0000000..da24886
>>>> --- /dev/null
>>>> +++ b/arch/metag/include/asm/irq.h
>>>> +#ifndef HW_IRQS
>>>> +#define HW_IRQS  (HWSTATMETA_OFFSET_MAX + HWSTATEXT_OFFSET_MAX + \
>>>> +		HWSTATEXT2_OFFSET_MAX + HWSTATEXT4_OFFSET_MAX + \
>>>> +		HWSTATEXT6_OFFSET_MAX)
>>>> +#endif
>>>> +
>>>> +#define META_IRQS 32
>>>> +
>>>> +#define NR_IRQS (META_IRQS + HW_IRQS)
>>>
>>> I think you should use sparse IRQs right from the start and avoid
>>> hardcoding interrupt numbers from the start.
>>
>> I presume core irq numbers need hard coding (e.g. those required by core
>> code like cross-hardware-thread irqs and perf counters)? Or would the
>> recommended way be to have a function which is given the core hw irq
>> number and converts/maps a virtual irq for it using the irq domain?
> 
> Anything that is probed as a platform device can get the interrupt numbers
> using the device resources that are filled in by the device tree probe.
> 
> If you need something earlier, you can call irq_of_parse_and_map()
> manually.

So does that leave any way for a non-GPL driver to simply get an IRQ
number? Reading the code I can't find a way other than using legacy irq
domains (are they acceptable for new architecture ports?).
* I think the usual way is to specify an IRQ in a module parameter, but
that no longer makes any sense as it would need mapping.
irq_create_mapping could have worked (maybe taking advantage of the
irq_default_domain), but it's EXPORT_SYMBOL_GPL.
* Using a platform driver isn't possible because
platform_driver_register and friends are EXPORT_SYMBOL_GPL.
* Similarly irq_of_parse_and_map is EXPORT_SYMBOL_GPL (except for sparc
curiously enough).

So what would be the expected way to do that with linear irq domains?

Disclaimer: I don't maintain or work on such drivers, but like other
architectures in the kernel the ability needs to be supported one way or
another.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 17/40] metag: IRQ handling
  2012-12-18 13:25         ` James Hogan
@ 2012-12-19 15:58           ` Arnd Bergmann
  2012-12-20 10:14             ` James Hogan
  0 siblings, 1 reply; 116+ messages in thread
From: Arnd Bergmann @ 2012-12-19 15:58 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, Grant

On Tuesday 18 December 2012, James Hogan wrote:
> > Anything that is probed as a platform device can get the interrupt numbers
> > using the device resources that are filled in by the device tree probe.
> > 
> > If you need something earlier, you can call irq_of_parse_and_map()
> > manually.
> 
> So does that leave any way for a non-GPL driver to simply get an IRQ
> number? Reading the code I can't find a way other than using legacy irq
> domains (are they acceptable for new architecture ports?).

Yes, I think that is correct. I assume it's also intentional, but I am
not completely sure since it is not my code.

> * I think the usual way is to specify an IRQ in a module parameter, but
> that no longer makes any sense as it would need mapping.

That is also a really bad interface in general. We stopped doing it this
way after linux-2.4 for in-kernel drivers.

> irq_create_mapping could have worked (maybe taking advantage of the
> irq_default_domain), but it's EXPORT_SYMBOL_GPL.
> * Using a platform driver isn't possible because
> platform_driver_register and friends are EXPORT_SYMBOL_GPL.
> * Similarly irq_of_parse_and_map is EXPORT_SYMBOL_GPL (except for sparc
> curiously enough).

Interestingly, the amba_driver interfaces don't have the GPL annotation.
I assume that at the time when platform_driver was introduced, nobody
considered the possibility that there would be a goood reason to
have this used in drivers ported from other operating systems under
an incompatible license. It's probably impossible to change that
now, given the number of individual contributions that have been
added to the platform driver infrastructure over the years.

> So what would be the expected way to do that with linear irq domains?

I think the best answer to that is "you're screwed".  There may be
a way to change the interfaces to just EXPORT_SYMBOL if you can make
a good case about a specific driver needing it, but my suspicion is
that that's not likely to be successful when coming from an imgtec.com
address or without details about the driver that needs it ;-)

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 17/40] metag: IRQ handling
  2012-12-19 15:58           ` Arnd Bergmann
@ 2012-12-20 10:14             ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2012-12-20 10:14 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch, Grant

On 19/12/12 15:58, Arnd Bergmann wrote:
> On Tuesday 18 December 2012, James Hogan wrote:
>> So what would be the expected way to do that with linear irq domains?
> 
> I think the best answer to that is "you're screwed".  There may be
> a way to change the interfaces to just EXPORT_SYMBOL if you can make
> a good case about a specific driver needing it, but my suspicion is
> that that's not likely to be successful when coming from an imgtec.com
> address or without details about the driver that needs it ;-)

:-)
Well after further research it appears all the drivers I had in mind
will be GPL anyway, so it shouldn't be a problem after all.

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
  2012-11-23 16:47         ` Arnd Bergmann
@ 2013-01-09 16:04             ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2013-01-09 16:04 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch, linux-kernel

On 23/11/12 16:47, Arnd Bergmann wrote:
> On Friday 23 November 2012, James Hogan wrote:
>> Subject: [PATCH 1/1] asm-generic/io.h: check CONFIG_VIRT_TO_BUS
>>
>> Make asm-generic/io.h check CONFIG_VIRT_TO_BUS before defining
>> virt_to_bus() and bus_to_virt(), otherwise it's easy to accidentally
>> have a silently failing incorrect direct mapped definition rather then
>> no definition at all.
>>
>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> 
> Good catch!
> 
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> 

Hi Arnd,

Would you like me to include this in the meta patchset or is it okay for
you to take it through the asm-generic tree? (I can resend separately if
you like)

Thanks
James


^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
@ 2013-01-09 16:04             ` James Hogan
  0 siblings, 0 replies; 116+ messages in thread
From: James Hogan @ 2013-01-09 16:04 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-arch, linux-kernel

On 23/11/12 16:47, Arnd Bergmann wrote:
> On Friday 23 November 2012, James Hogan wrote:
>> Subject: [PATCH 1/1] asm-generic/io.h: check CONFIG_VIRT_TO_BUS
>>
>> Make asm-generic/io.h check CONFIG_VIRT_TO_BUS before defining
>> virt_to_bus() and bus_to_virt(), otherwise it's easy to accidentally
>> have a silently failing incorrect direct mapped definition rather then
>> no definition at all.
>>
>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> 
> Good catch!
> 
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> 

Hi Arnd,

Would you like me to include this in the meta patchset or is it okay for
you to take it through the asm-generic tree? (I can resend separately if
you like)

Thanks
James

^ permalink raw reply	[flat|nested] 116+ messages in thread

* Re: [RFC PATCH v1 24/40] metag: DMA
  2013-01-09 16:04             ` James Hogan
  (?)
@ 2013-01-09 16:08             ` Arnd Bergmann
  -1 siblings, 0 replies; 116+ messages in thread
From: Arnd Bergmann @ 2013-01-09 16:08 UTC (permalink / raw)
  To: James Hogan; +Cc: linux-arch, linux-kernel

On Wednesday 09 January 2013, James Hogan wrote:
> Would you like me to include this in the meta patchset or is it okay for
> you to take it through the asm-generic tree? (I can resend separately if
> you like)

I usually prefer patches to go through the tree of whoever needs them
for asm-generic.

	Arnd

^ permalink raw reply	[flat|nested] 116+ messages in thread

end of thread, other threads:[~2013-01-09 16:09 UTC | newest]

Thread overview: 116+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-31 16:13 [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 01/40] asm-generic/io.h: remove asm/cacheflush.h include James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 02/40] asm-generic/unistd.h: handle symbol prefixes in cond_syscall James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 03/40] Add CONFIG_HAVE_64BIT_ALIGNED_STRUCT for taskstats James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 04/40] trace/ring_buffer: handle 64bit aligned structs James Hogan
2012-10-31 17:59   ` Steven Rostedt
2012-10-31 18:19     ` James Hogan
2012-11-01 17:32     ` Will Newton
2012-11-01 19:30       ` Steven Rostedt
2012-10-31 16:13 ` [RFC PATCH v1 05/40] Revert some of "binfmt_elf: cleanups" James Hogan
2012-10-31 16:13   ` James Hogan
2012-11-01 13:25   ` Mikael Pettersson
2012-11-01 13:25     ` Mikael Pettersson
2012-11-01 13:52     ` James Hogan
2012-11-01 13:52       ` James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 06/40] of/vendor-prefixes: add Imagination Technologies James Hogan
2012-11-01  1:38   ` Rob Herring
2012-11-01  9:20     ` James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 07/40] metag: Add MAINTAINERS entry James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 08/40] metag: Boot James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 11/40] metag: Signal handling James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 12/40] metag: Build infrastructure James Hogan
2012-10-31 19:35   ` Sam Ravnborg
2012-10-31 21:34     ` James Hogan
2012-11-02  9:40     ` James Hogan
2012-11-02 17:01       ` Sam Ravnborg
2012-11-09 14:46   ` Arnd Bergmann
2012-11-09 14:55     ` James Hogan
2012-11-20 15:06     ` James Hogan
2012-11-20 15:43       ` Arnd Bergmann
2012-11-20 16:01         ` James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 13/40] metag: Device tree James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 14/40] metag: Ptrace James Hogan
2012-11-08  7:17   ` Jonas Bonn
2012-11-09  9:26     ` James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 15/40] metag: Time keeping James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 16/40] metag: Traps James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 17/40] metag: IRQ handling James Hogan
2012-11-09 14:12   ` Arnd Bergmann
2012-11-20 16:08     ` James Hogan
2012-11-20 16:15       ` Arnd Bergmann
2012-12-18 13:25         ` James Hogan
2012-12-19 15:58           ` Arnd Bergmann
2012-12-20 10:14             ` James Hogan
2012-10-31 16:13 ` [RFC PATCH v1 18/40] metag: System Calls James Hogan
2012-11-09 14:20   ` Arnd Bergmann
2012-11-22 12:01     ` James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 19/40] metag: Scheduling/Process management James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 20/40] metag: Module support James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 21/40] metag: Atomics, locks and bitops James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 22/40] metag: Basic documentation James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 23/40] metag: SMP support James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 24/40] metag: DMA James Hogan
2012-11-09 14:25   ` Arnd Bergmann
2012-11-23 15:53     ` James Hogan
2012-11-23 16:20       ` James Hogan
2012-11-23 16:20         ` James Hogan
2012-11-23 16:47         ` Arnd Bergmann
2013-01-09 16:04           ` James Hogan
2013-01-09 16:04             ` James Hogan
2013-01-09 16:08             ` Arnd Bergmann
2012-11-23 16:47       ` Arnd Bergmann
2012-10-31 16:14 ` [RFC PATCH v1 25/40] metag: optimised library functions James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 26/40] metag: Stack unwinding James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 27/40] metag: various other headers James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 28/40] metag: Perf James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 29/40] metag: ftrace support James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 30/40] scripts/checkstack.pl: Add metag support James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 31/40] char: don't build rtc or genrtc on METAG James Hogan
2012-11-01 15:39   ` Greg KH
2012-11-01 17:33     ` James Hogan
2012-11-09 14:38       ` Arnd Bergmann
2012-10-31 16:14 ` [RFC PATCH v1 32/40] i8042: don't build " James Hogan
2012-11-09 14:28   ` Arnd Bergmann
2012-11-16 11:06     ` James Hogan
2012-11-16 15:20       ` Arnd Bergmann
2012-11-16 16:19         ` Geert Uytterhoeven
2012-11-16 16:43           ` Arnd Bergmann
2012-11-16 16:50         ` James Hogan
2012-11-16 17:20           ` Arnd Bergmann
2012-10-31 16:14 ` [RFC PATCH v1 33/40] parport: " James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 34/40] musb: don't redefine {read,write}s{l,w,b} on metag James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 35/40] vga console: don't build on METAG James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 36/40] metag: OProfile James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 37/40] metag: Various sysfs drivers James Hogan
2012-10-31 19:30   ` Greg KH
2012-10-31 19:41     ` James Hogan
2012-11-09 14:32       ` Arnd Bergmann
2012-11-09 14:35         ` James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 38/40] metag: add JTAG Debug Adapter (DA) support James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 39/40] tty/metag_da: add metag DA TTY driver James Hogan
2012-10-31 16:14 ` [RFC PATCH v1 40/40] fs: dafs: Add DAFS filesystem for metag James Hogan
2012-10-31 16:14   ` James Hogan
2012-10-31 16:42   ` Al Viro
2012-10-31 19:39     ` James Hogan
2012-10-31 19:39       ` James Hogan
2012-10-31 19:55   ` Myklebust, Trond
2012-10-31 21:39     ` James Hogan
2012-11-02  9:33     ` James Hogan
2012-10-31 16:26 ` [RFC PATCH v1 00/40] Meta Linux Kernel Port James Hogan
2012-11-09 15:06   ` Arnd Bergmann
2012-11-09 15:21     ` James Hogan
2012-11-09 15:58       ` Arnd Bergmann
2012-11-09 16:20         ` James Hogan
2012-11-09 21:55           ` Arnd Bergmann
2012-11-09 23:28             ` James Hogan
2012-11-12 16:59           ` James Hogan
2012-11-12 19:10             ` Arnd Bergmann
2012-11-13  9:43               ` James Hogan
2012-11-13  9:55                 ` Arnd Bergmann
2012-10-31 22:33 ` Tony Breeds
2012-11-01 11:42   ` James Hogan
2012-11-01 23:28     ` Tony Breeds
2012-11-09 16:52     ` James Hogan
2012-11-09 15:16 ` Arnd Bergmann
2012-11-09 16:06   ` James Hogan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.