All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/43] Meta Linux Kernel Port
@ 2013-01-29 14:15 ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch; +Cc: Arnd Bergmann, James Hogan

This patchset adds core architecture support to Linux for Imagination's
Meta ATP (Meta 1) and HTP (Meta 2) processor cores. I'm now happy with
this patchset as it is. Not a lot has changed since v3 (see below),
which is why I've only included patches which have changed in
non-trivial ways, namely patches 19, 21, 26, 42, 43, but of course
further review and acks are welcome.

The patches are based on v3.8-rc5, and can be found in a more complete
form 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.

A buildroot tree containing toolchain patches can be found here (use
meta2_defconfig):
  git://github.com/img-meta/metag-buildroot.git metag-core

Freely available instruction set and architecture overview documents can
be found on the following page:
  http://www.imgtec.com/downloads.asp

---
v4 (tag: metag-core-v4):

changes since v3
* tree:
  * rebased on v3.8-rc5
  * make more use of __core_reg_{get,set} interfaces over inline asm
  * removed the oprofile patch for now until I've looked at wrapping it
    around perf as some other architectures do
* timekeeping
  * move to drivers/clocksource/metag_generic.c (Arnd)
  * use cpu notifier to setup secondary cpu timers
* traps
  * fix lack of critical section restart in kick_handler
* irq handling
  * rename irqchips s/meta/metag/
* scheduling/process management
  * fix idle race (requires use of inline asm to trigger irq entry
    rather than SWITCH instruction) (Arnd)
* build infrastructure & device tree
  * move dtb directory into dts and use common dtb rules (Rob Herring)
* ftrace:
  * remove duplicate include (Steven Rostedt)
* tty/metag_da:
  * addressed review comments (Jiri Slaby)
* fs/imgdafs:
  * clean up fscall similarly to chancall in tty/metag_da


v3 (tag: metag-core-v3):

changes since v2
* tree:
  * rebased on linux-next and related changes
  * integrated generic kernel_thread/kernel_execve into patches
* signals:
  * replace do_notify_resume with do_work_pending containing signal
    handling loop (Al Viro)
  * handlerless local syscall restart (Al Viro)
  * fix rt_sigreturn's do_sigaltstack handling (Al Viro)
  * explicitly detect rt_sigreturn and treat as non-syscall for purposes
    of syscall restart (Al Viro)
  * refactor syscall restart code based on arm
  * use generic sigaltstack
* irq handling:
  * move irq drivers into drivers/irqchip/ (Arnd)
  * switch to sparse irq (Arnd)
  * allow per machine override of nr_irqs
  * switch root TBI, internal, and external interrupt drivers to linear
    irqdomain
  * implement masking in internal interrupt driver
* system calls:
  * remove magic pt_regs 7th syscall argument (Al Viro)
  * use generic sys_clone (Al Viro)
  * fix use of 0 for invalid syscall number (use -1 instead)
* documentation
  * added Documentation/kernel-ABI.txt describing various ABIs (Al Viro)
* tbx:
  * move tbx/tbicache.c to mm/cache.c and refactor / clean up (Joe
    Perches)
  * clean up tbx/tbistring.c
  * rename asm/lock.h->asm/global_lock.h, remove dependence on tbx
  * update all lock users to use asm/global_lock.h rather than tbx
    macros
  * add and use __core_reg_[gs]et macros to replace TBI_[GS]ETREG
  * make use of compiler builtins for cache/tlb handling instructions
    rather than tbx macros
* tty/metag_da:
  * mostly rewritten to address Alan Cox' feedback
  * use del_timer_sync() where appropriate (Alan Cox)
  * convert to use tty_port helpers (Alan Cox)
  * fix tty kref safety (Alan Cox)
  * implement proper output buffering (Alan Cox)
  * fix init error handling
  * implement hangup (Alan Cox)
  * omit specific baud rate (Alan Cox)
* boot:
  * switch to generic CMDLINE Kconfig symbols (Sam Ravnborg)
* alignment patches
  * removed taskstats changes (see "netlink: align attributes on
    64-bits" patches)
  * rework CONFIG_HAVE_64BIT_ALIGNED_ACCESS patch for clarity


v2 (tag: metag-core-v2):

changes based on rfc-v1 review feedback:
* moved build infrastructure patch later (Sam Ravnborg)
* various build system cleanups (Sam Ravnborg)
* split generic build changes out of main build patch (Sam Ravnborg)
* deprecate powervr DT vendor id (Rob Herring)
* ftrace now uses recordmcount.c (Steven Rostedt)
* fixed imgdafs fserrno problem (Al Viro)
* io.h changes, inline asm, removed casts, added metag_in/out (Arnd)
* remove bus_to_virt (Arnd)
* use asm-generic/syscalls.h (Arnd)
* remove superfluous metag_spinlock system call (Arnd)
* renamed packed 64bit arg syscall wrappers (Arnd)

other changes:
* rebased on v3.7-rc8
  * changes based on v3.6..v3.7-rc8 patches
  * uapi disintegration (thanks to David Howells for the assistance)
* add cmpdi2/ucmpdi2 intrinsics and clean up lib Makefile
* fix numa build
* split memory management patch into 6 separate patches to make them
  more manageable and stop them bouncing:
  * cache/tlb handling
  * memory management
  * memory handling
  * huge tlb
  * highmem
  * tcm
* split architecture definitions out of TBX into asm/metag_*.h
* merged remaining TBX header files into a single asm/tbx.h
* split TBX patch into 2 separate patches to stop them bouncing
* merge various similar Kconfig changes in generic code
* dropped sysfs drivers from this patchset (we normally have them turned
  off anyway so they're not a priority)
* renamed dafs to imgdafs
* implemented perf events
* rewrote ptrace to use nicely abstracted regsets
* removed non-standard hw_breakpoints/ptrace API
* use ptrace abstracted context structs in sigcontext
* update DA TTY to be more thread safe and use tty_ports
* switch to generic sys_execve

James Hogan (43):
  asm-generic/io.h: check CONFIG_VIRT_TO_BUS
  asm-generic/unistd.h: handle symbol prefixes in cond_syscall
  Revert some of "binfmt_elf: cleanups"
  Add HAVE_64BIT_ALIGNED_ACCESS
  trace/ring_buffer: handle 64bit aligned structs
  metag: Add MAINTAINERS entry
  metag: Headers for core arch constants
  metag: Header for core memory mapped registers
  metag: Boot
  metag: TBX header
  metag: TBX source
  metag: Cache/TLB handling
  metag: Memory management
  metag: Memory handling
  metag: Huge TLB
  metag: Highmem support
  metag: TCM support
  metag: Signal handling
  metag: Device tree
  metag: ptrace
  metag: Time keeping
  metag: Traps
  metag: IRQ handling
  metag: Internal and external irqchips
  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
  mm: define VM_GROWSUP for CONFIG_METAG
  Kconfig.debug: add METAG to dependency lists
  metag: Build infrastructure
  metag: Perf
  metag: ftrace support
  scripts/checkstack.pl: Add metag support
  metag: Add JTAG Debug Adapter (DA) support
  tty/metag_da: Add metag DA TTY driver
  fs: imgdafs: Add IMG DAFS filesystem for metag

 Documentation/00-INDEX                             |    2 +
 .../devicetree/bindings/metag/meta-intc.txt        |   82 ++
 Documentation/kernel-parameters.txt                |    4 +
 Documentation/metag/00-INDEX                       |    4 +
 Documentation/metag/kernel-ABI.txt                 |  256 ++++
 MAINTAINERS                                        |   12 +
 arch/Kconfig                                       |   16 +
 arch/metag/Kconfig                                 |  295 ++++
 arch/metag/Kconfig.debug                           |   40 +
 arch/metag/Kconfig.soc                             |   55 +
 arch/metag/Makefile                                |   87 ++
 arch/metag/boot/.gitignore                         |    4 +
 arch/metag/boot/Makefile                           |   68 +
 arch/metag/boot/dts/Makefile                       |   16 +
 arch/metag/boot/dts/skeleton.dts                   |   10 +
 arch/metag/boot/dts/skeleton.dtsi                  |   14 +
 arch/metag/configs/meta1_defconfig                 |   41 +
 arch/metag/configs/meta2_defconfig                 |   42 +
 arch/metag/configs/meta2_smp_defconfig             |   43 +
 arch/metag/include/asm/Kbuild                      |   54 +
 arch/metag/include/asm/atomic.h                    |   53 +
 arch/metag/include/asm/atomic_lnkget.h             |  234 ++++
 arch/metag/include/asm/atomic_lock1.h              |  160 +++
 arch/metag/include/asm/barrier.h                   |   85 ++
 arch/metag/include/asm/bitops.h                    |  132 ++
 arch/metag/include/asm/bug.h                       |   12 +
 arch/metag/include/asm/cache.h                     |   23 +
 arch/metag/include/asm/cacheflush.h                |  250 ++++
 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_reg.h                  |   35 +
 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                       |  128 ++
 arch/metag/include/asm/fixmap.h                    |   99 ++
 arch/metag/include/asm/ftrace.h                    |   23 +
 arch/metag/include/asm/global_lock.h               |  100 ++
 arch/metag/include/asm/gpio.h                      |    4 +
 arch/metag/include/asm/highmem.h                   |   62 +
 arch/metag/include/asm/hugetlb.h                   |   86 ++
 arch/metag/include/asm/hwthread.h                  |   40 +
 arch/metag/include/asm/io.h                        |  165 +++
 arch/metag/include/asm/irq.h                       |   32 +
 arch/metag/include/asm/irqflags.h                  |   94 ++
 arch/metag/include/asm/l2cache.h                   |  258 ++++
 arch/metag/include/asm/linkage.h                   |    7 +
 arch/metag/include/asm/mach/arch.h                 |   86 ++
 arch/metag/include/asm/metag_isa.h                 |   81 ++
 arch/metag/include/asm/metag_mem.h                 | 1106 +++++++++++++++
 arch/metag/include/asm/metag_regs.h                | 1180 ++++++++++++++++
 arch/metag/include/asm/mman.h                      |   11 +
 arch/metag/include/asm/mmu.h                       |   77 ++
 arch/metag/include/asm/mmu_context.h               |  113 ++
 arch/metag/include/asm/mmzone.h                    |   42 +
 arch/metag/include/asm/module.h                    |   37 +
 arch/metag/include/asm/page.h                      |  128 ++
 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                 |  202 +++
 arch/metag/include/asm/prom.h                      |   23 +
 arch/metag/include/asm/ptrace.h                    |   60 +
 arch/metag/include/asm/setup.h                     |    8 +
 arch/metag/include/asm/smp.h                       |   29 +
 arch/metag/include/asm/sparsemem.h                 |   13 +
 arch/metag/include/asm/spinlock.h                  |   22 +
 arch/metag/include/asm/spinlock_lnkget.h           |  249 ++++
 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/switch.h                    |   21 +
 arch/metag/include/asm/syscall.h                   |  104 ++
 arch/metag/include/asm/syscalls.h                  |   39 +
 arch/metag/include/asm/tbx.h                       | 1425 ++++++++++++++++++++
 arch/metag/include/asm/tcm.h                       |   30 +
 arch/metag/include/asm/thread_info.h               |  155 +++
 arch/metag/include/asm/tlb.h                       |   36 +
 arch/metag/include/asm/tlbflush.h                  |   77 ++
 arch/metag/include/asm/topology.h                  |   53 +
 arch/metag/include/asm/traps.h                     |   48 +
 arch/metag/include/asm/uaccess.h                   |  241 ++++
 arch/metag/include/asm/unistd.h                    |   12 +
 arch/metag/include/asm/user_gateway.h              |   44 +
 arch/metag/include/uapi/asm/Kbuild                 |   13 +
 arch/metag/include/uapi/asm/byteorder.h            |    1 +
 arch/metag/include/uapi/asm/ptrace.h               |  113 ++
 arch/metag/include/uapi/asm/resource.h             |    7 +
 arch/metag/include/uapi/asm/sigcontext.h           |   31 +
 arch/metag/include/uapi/asm/siginfo.h              |    8 +
 arch/metag/include/uapi/asm/swab.h                 |   26 +
 arch/metag/include/uapi/asm/unistd.h               |   21 +
 arch/metag/kernel/.gitignore                       |    1 +
 arch/metag/kernel/Makefile                         |   39 +
 arch/metag/kernel/asm-offsets.c                    |   14 +
 arch/metag/kernel/cachepart.c                      |  124 ++
 arch/metag/kernel/clock.c                          |   53 +
 arch/metag/kernel/core_reg.c                       |  117 ++
 arch/metag/kernel/da.c                             |   23 +
 arch/metag/kernel/devtree.c                        |  100 ++
 arch/metag/kernel/dma.c                            |  507 +++++++
 arch/metag/kernel/ftrace.c                         |  126 ++
 arch/metag/kernel/ftrace_stub.S                    |   76 ++
 arch/metag/kernel/head.S                           |   57 +
 arch/metag/kernel/irq.c                            |  323 +++++
 arch/metag/kernel/kick.c                           |   98 ++
 arch/metag/kernel/machines.c                       |   20 +
 arch/metag/kernel/metag_ksyms.c                    |   80 ++
 arch/metag/kernel/module.c                         |  284 ++++
 arch/metag/kernel/perf/Makefile                    |    3 +
 arch/metag/kernel/perf/perf_event.c                |  861 ++++++++++++
 arch/metag/kernel/perf/perf_event.h                |  106 ++
 arch/metag/kernel/perf_callchain.c                 |   96 ++
 arch/metag/kernel/process.c                        |  461 +++++++
 arch/metag/kernel/ptrace.c                         |  380 ++++++
 arch/metag/kernel/setup.c                          |  571 ++++++++
 arch/metag/kernel/signal.c                         |  344 +++++
 arch/metag/kernel/smp.c                            |  575 ++++++++
 arch/metag/kernel/stacktrace.c                     |  187 +++
 arch/metag/kernel/sys_metag.c                      |  180 +++
 arch/metag/kernel/tbiunexp.S                       |   22 +
 arch/metag/kernel/tcm.c                            |  151 +++
 arch/metag/kernel/time.c                           |   15 +
 arch/metag/kernel/topology.c                       |   77 ++
 arch/metag/kernel/traps.c                          |  978 ++++++++++++++
 arch/metag/kernel/user_gateway.S                   |   97 ++
 arch/metag/kernel/vmlinux.lds.S                    |   71 +
 arch/metag/lib/Makefile                            |   22 +
 arch/metag/lib/ashldi3.S                           |   33 +
 arch/metag/lib/ashrdi3.S                           |   33 +
 arch/metag/lib/checksum.c                          |  168 +++
 arch/metag/lib/clear_page.S                        |   17 +
 arch/metag/lib/cmpdi2.S                            |   32 +
 arch/metag/lib/copy_page.S                         |   20 +
 arch/metag/lib/delay.c                             |   56 +
 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/ucmpdi2.S                           |   27 +
 arch/metag/lib/usercopy.c                          | 1341 ++++++++++++++++++
 arch/metag/mm/Kconfig                              |  153 +++
 arch/metag/mm/Makefile                             |   19 +
 arch/metag/mm/cache.c                              |  441 ++++++
 arch/metag/mm/extable.c                            |   15 +
 arch/metag/mm/fault.c                              |  239 ++++
 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                            |  192 +++
 arch/metag/mm/maccess.c                            |   68 +
 arch/metag/mm/mmu-meta1.c                          |  157 +++
 arch/metag/mm/mmu-meta2.c                          |  207 +++
 arch/metag/mm/numa.c                               |   81 ++
 arch/metag/tbx/Makefile                            |   21 +
 arch/metag/tbx/tbicore.S                           |  136 ++
 arch/metag/tbx/tbictx.S                            |  366 +++++
 arch/metag/tbx/tbictxfpu.S                         |  190 +++
 arch/metag/tbx/tbidefr.S                           |  175 +++
 arch/metag/tbx/tbidspram.S                         |  161 +++
 arch/metag/tbx/tbilogf.S                           |   48 +
 arch/metag/tbx/tbipcx.S                            |  451 +++++++
 arch/metag/tbx/tbiroot.S                           |   87 ++
 arch/metag/tbx/tbisoft.S                           |  237 ++++
 arch/metag/tbx/tbistring.c                         |  114 ++
 arch/metag/tbx/tbitimer.S                          |  207 +++
 drivers/clocksource/Kconfig                        |    5 +
 drivers/clocksource/Makefile                       |    1 +
 drivers/clocksource/metag_generic.c                |  198 +++
 drivers/irqchip/Makefile                           |    2 +
 drivers/irqchip/irq-metag-ext.c                    |  868 ++++++++++++
 drivers/irqchip/irq-metag.c                        |  343 +++++
 drivers/tty/Kconfig                                |   13 +
 drivers/tty/Makefile                               |    1 +
 drivers/tty/metag_da.c                             |  679 ++++++++++
 fs/Kconfig                                         |    1 +
 fs/Makefile                                        |    1 +
 fs/binfmt_elf.c                                    |    4 +
 fs/imgdafs/Kconfig                                 |    6 +
 fs/imgdafs/Makefile                                |    7 +
 fs/imgdafs/imgdafs.h                               |   80 ++
 fs/imgdafs/inode.c                                 |  843 ++++++++++++
 include/asm-generic/io.h                           |    2 +
 include/asm-generic/unistd.h                       |    9 +-
 include/clocksource/metag_generic.h                |   21 +
 include/linux/irqchip/metag-ext.h                  |   33 +
 include/linux/irqchip/metag.h                      |   24 +
 include/linux/mm.h                                 |    2 +
 include/uapi/linux/elf.h                           |    2 +
 kernel/trace/ring_buffer.c                         |    6 +-
 lib/Kconfig.debug                                  |    4 +-
 scripts/checkstack.pl                              |    8 +-
 scripts/recordmcount.c                             |   13 +
 207 files changed, 28428 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/metag/meta-intc.txt
 create mode 100644 Documentation/metag/00-INDEX
 create mode 100644 Documentation/metag/kernel-ABI.txt
 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/dts/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/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_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/global_lock.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/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/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/mach/arch.h
 create mode 100644 arch/metag/include/asm/metag_isa.h
 create mode 100644 arch/metag/include/asm/metag_mem.h
 create mode 100644 arch/metag/include/asm/metag_regs.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/setup.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/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.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/include/uapi/asm/Kbuild
 create mode 100644 arch/metag/include/uapi/asm/byteorder.h
 create mode 100644 arch/metag/include/uapi/asm/ptrace.h
 create mode 100644 arch/metag/include/uapi/asm/resource.h
 create mode 100644 arch/metag/include/uapi/asm/sigcontext.h
 create mode 100644 arch/metag/include/uapi/asm/siginfo.h
 create mode 100644 arch/metag/include/uapi/asm/swab.h
 create mode 100644 arch/metag/include/uapi/asm/unistd.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/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/irq.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/Makefile
 create mode 100644 arch/metag/kernel/perf/perf_event.c
 create mode 100644 arch/metag/kernel/perf/perf_event.h
 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/cmpdi2.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/ucmpdi2.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/cache.c
 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/tbx/Makefile
 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/clocksource/metag_generic.c
 create mode 100644 drivers/irqchip/irq-metag-ext.c
 create mode 100644 drivers/irqchip/irq-metag.c
 create mode 100644 drivers/tty/metag_da.c
 create mode 100644 fs/imgdafs/Kconfig
 create mode 100644 fs/imgdafs/Makefile
 create mode 100644 fs/imgdafs/imgdafs.h
 create mode 100644 fs/imgdafs/inode.c
 create mode 100644 include/clocksource/metag_generic.h
 create mode 100644 include/linux/irqchip/metag-ext.h
 create mode 100644 include/linux/irqchip/metag.h

-- 
1.7.7.6



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

* [PATCH v4 00/43] Meta Linux Kernel Port
@ 2013-01-29 14:15 ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch; +Cc: Arnd Bergmann, James Hogan

This patchset adds core architecture support to Linux for Imagination's
Meta ATP (Meta 1) and HTP (Meta 2) processor cores. I'm now happy with
this patchset as it is. Not a lot has changed since v3 (see below),
which is why I've only included patches which have changed in
non-trivial ways, namely patches 19, 21, 26, 42, 43, but of course
further review and acks are welcome.

The patches are based on v3.8-rc5, and can be found in a more complete
form 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.

A buildroot tree containing toolchain patches can be found here (use
meta2_defconfig):
  git://github.com/img-meta/metag-buildroot.git metag-core

Freely available instruction set and architecture overview documents can
be found on the following page:
  http://www.imgtec.com/downloads.asp

---
v4 (tag: metag-core-v4):

changes since v3
* tree:
  * rebased on v3.8-rc5
  * make more use of __core_reg_{get,set} interfaces over inline asm
  * removed the oprofile patch for now until I've looked at wrapping it
    around perf as some other architectures do
* timekeeping
  * move to drivers/clocksource/metag_generic.c (Arnd)
  * use cpu notifier to setup secondary cpu timers
* traps
  * fix lack of critical section restart in kick_handler
* irq handling
  * rename irqchips s/meta/metag/
* scheduling/process management
  * fix idle race (requires use of inline asm to trigger irq entry
    rather than SWITCH instruction) (Arnd)
* build infrastructure & device tree
  * move dtb directory into dts and use common dtb rules (Rob Herring)
* ftrace:
  * remove duplicate include (Steven Rostedt)
* tty/metag_da:
  * addressed review comments (Jiri Slaby)
* fs/imgdafs:
  * clean up fscall similarly to chancall in tty/metag_da


v3 (tag: metag-core-v3):

changes since v2
* tree:
  * rebased on linux-next and related changes
  * integrated generic kernel_thread/kernel_execve into patches
* signals:
  * replace do_notify_resume with do_work_pending containing signal
    handling loop (Al Viro)
  * handlerless local syscall restart (Al Viro)
  * fix rt_sigreturn's do_sigaltstack handling (Al Viro)
  * explicitly detect rt_sigreturn and treat as non-syscall for purposes
    of syscall restart (Al Viro)
  * refactor syscall restart code based on arm
  * use generic sigaltstack
* irq handling:
  * move irq drivers into drivers/irqchip/ (Arnd)
  * switch to sparse irq (Arnd)
  * allow per machine override of nr_irqs
  * switch root TBI, internal, and external interrupt drivers to linear
    irqdomain
  * implement masking in internal interrupt driver
* system calls:
  * remove magic pt_regs 7th syscall argument (Al Viro)
  * use generic sys_clone (Al Viro)
  * fix use of 0 for invalid syscall number (use -1 instead)
* documentation
  * added Documentation/kernel-ABI.txt describing various ABIs (Al Viro)
* tbx:
  * move tbx/tbicache.c to mm/cache.c and refactor / clean up (Joe
    Perches)
  * clean up tbx/tbistring.c
  * rename asm/lock.h->asm/global_lock.h, remove dependence on tbx
  * update all lock users to use asm/global_lock.h rather than tbx
    macros
  * add and use __core_reg_[gs]et macros to replace TBI_[GS]ETREG
  * make use of compiler builtins for cache/tlb handling instructions
    rather than tbx macros
* tty/metag_da:
  * mostly rewritten to address Alan Cox' feedback
  * use del_timer_sync() where appropriate (Alan Cox)
  * convert to use tty_port helpers (Alan Cox)
  * fix tty kref safety (Alan Cox)
  * implement proper output buffering (Alan Cox)
  * fix init error handling
  * implement hangup (Alan Cox)
  * omit specific baud rate (Alan Cox)
* boot:
  * switch to generic CMDLINE Kconfig symbols (Sam Ravnborg)
* alignment patches
  * removed taskstats changes (see "netlink: align attributes on
    64-bits" patches)
  * rework CONFIG_HAVE_64BIT_ALIGNED_ACCESS patch for clarity


v2 (tag: metag-core-v2):

changes based on rfc-v1 review feedback:
* moved build infrastructure patch later (Sam Ravnborg)
* various build system cleanups (Sam Ravnborg)
* split generic build changes out of main build patch (Sam Ravnborg)
* deprecate powervr DT vendor id (Rob Herring)
* ftrace now uses recordmcount.c (Steven Rostedt)
* fixed imgdafs fserrno problem (Al Viro)
* io.h changes, inline asm, removed casts, added metag_in/out (Arnd)
* remove bus_to_virt (Arnd)
* use asm-generic/syscalls.h (Arnd)
* remove superfluous metag_spinlock system call (Arnd)
* renamed packed 64bit arg syscall wrappers (Arnd)

other changes:
* rebased on v3.7-rc8
  * changes based on v3.6..v3.7-rc8 patches
  * uapi disintegration (thanks to David Howells for the assistance)
* add cmpdi2/ucmpdi2 intrinsics and clean up lib Makefile
* fix numa build
* split memory management patch into 6 separate patches to make them
  more manageable and stop them bouncing:
  * cache/tlb handling
  * memory management
  * memory handling
  * huge tlb
  * highmem
  * tcm
* split architecture definitions out of TBX into asm/metag_*.h
* merged remaining TBX header files into a single asm/tbx.h
* split TBX patch into 2 separate patches to stop them bouncing
* merge various similar Kconfig changes in generic code
* dropped sysfs drivers from this patchset (we normally have them turned
  off anyway so they're not a priority)
* renamed dafs to imgdafs
* implemented perf events
* rewrote ptrace to use nicely abstracted regsets
* removed non-standard hw_breakpoints/ptrace API
* use ptrace abstracted context structs in sigcontext
* update DA TTY to be more thread safe and use tty_ports
* switch to generic sys_execve

James Hogan (43):
  asm-generic/io.h: check CONFIG_VIRT_TO_BUS
  asm-generic/unistd.h: handle symbol prefixes in cond_syscall
  Revert some of "binfmt_elf: cleanups"
  Add HAVE_64BIT_ALIGNED_ACCESS
  trace/ring_buffer: handle 64bit aligned structs
  metag: Add MAINTAINERS entry
  metag: Headers for core arch constants
  metag: Header for core memory mapped registers
  metag: Boot
  metag: TBX header
  metag: TBX source
  metag: Cache/TLB handling
  metag: Memory management
  metag: Memory handling
  metag: Huge TLB
  metag: Highmem support
  metag: TCM support
  metag: Signal handling
  metag: Device tree
  metag: ptrace
  metag: Time keeping
  metag: Traps
  metag: IRQ handling
  metag: Internal and external irqchips
  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
  mm: define VM_GROWSUP for CONFIG_METAG
  Kconfig.debug: add METAG to dependency lists
  metag: Build infrastructure
  metag: Perf
  metag: ftrace support
  scripts/checkstack.pl: Add metag support
  metag: Add JTAG Debug Adapter (DA) support
  tty/metag_da: Add metag DA TTY driver
  fs: imgdafs: Add IMG DAFS filesystem for metag

 Documentation/00-INDEX                             |    2 +
 .../devicetree/bindings/metag/meta-intc.txt        |   82 ++
 Documentation/kernel-parameters.txt                |    4 +
 Documentation/metag/00-INDEX                       |    4 +
 Documentation/metag/kernel-ABI.txt                 |  256 ++++
 MAINTAINERS                                        |   12 +
 arch/Kconfig                                       |   16 +
 arch/metag/Kconfig                                 |  295 ++++
 arch/metag/Kconfig.debug                           |   40 +
 arch/metag/Kconfig.soc                             |   55 +
 arch/metag/Makefile                                |   87 ++
 arch/metag/boot/.gitignore                         |    4 +
 arch/metag/boot/Makefile                           |   68 +
 arch/metag/boot/dts/Makefile                       |   16 +
 arch/metag/boot/dts/skeleton.dts                   |   10 +
 arch/metag/boot/dts/skeleton.dtsi                  |   14 +
 arch/metag/configs/meta1_defconfig                 |   41 +
 arch/metag/configs/meta2_defconfig                 |   42 +
 arch/metag/configs/meta2_smp_defconfig             |   43 +
 arch/metag/include/asm/Kbuild                      |   54 +
 arch/metag/include/asm/atomic.h                    |   53 +
 arch/metag/include/asm/atomic_lnkget.h             |  234 ++++
 arch/metag/include/asm/atomic_lock1.h              |  160 +++
 arch/metag/include/asm/barrier.h                   |   85 ++
 arch/metag/include/asm/bitops.h                    |  132 ++
 arch/metag/include/asm/bug.h                       |   12 +
 arch/metag/include/asm/cache.h                     |   23 +
 arch/metag/include/asm/cacheflush.h                |  250 ++++
 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_reg.h                  |   35 +
 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                       |  128 ++
 arch/metag/include/asm/fixmap.h                    |   99 ++
 arch/metag/include/asm/ftrace.h                    |   23 +
 arch/metag/include/asm/global_lock.h               |  100 ++
 arch/metag/include/asm/gpio.h                      |    4 +
 arch/metag/include/asm/highmem.h                   |   62 +
 arch/metag/include/asm/hugetlb.h                   |   86 ++
 arch/metag/include/asm/hwthread.h                  |   40 +
 arch/metag/include/asm/io.h                        |  165 +++
 arch/metag/include/asm/irq.h                       |   32 +
 arch/metag/include/asm/irqflags.h                  |   94 ++
 arch/metag/include/asm/l2cache.h                   |  258 ++++
 arch/metag/include/asm/linkage.h                   |    7 +
 arch/metag/include/asm/mach/arch.h                 |   86 ++
 arch/metag/include/asm/metag_isa.h                 |   81 ++
 arch/metag/include/asm/metag_mem.h                 | 1106 +++++++++++++++
 arch/metag/include/asm/metag_regs.h                | 1180 ++++++++++++++++
 arch/metag/include/asm/mman.h                      |   11 +
 arch/metag/include/asm/mmu.h                       |   77 ++
 arch/metag/include/asm/mmu_context.h               |  113 ++
 arch/metag/include/asm/mmzone.h                    |   42 +
 arch/metag/include/asm/module.h                    |   37 +
 arch/metag/include/asm/page.h                      |  128 ++
 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                 |  202 +++
 arch/metag/include/asm/prom.h                      |   23 +
 arch/metag/include/asm/ptrace.h                    |   60 +
 arch/metag/include/asm/setup.h                     |    8 +
 arch/metag/include/asm/smp.h                       |   29 +
 arch/metag/include/asm/sparsemem.h                 |   13 +
 arch/metag/include/asm/spinlock.h                  |   22 +
 arch/metag/include/asm/spinlock_lnkget.h           |  249 ++++
 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/switch.h                    |   21 +
 arch/metag/include/asm/syscall.h                   |  104 ++
 arch/metag/include/asm/syscalls.h                  |   39 +
 arch/metag/include/asm/tbx.h                       | 1425 ++++++++++++++++++++
 arch/metag/include/asm/tcm.h                       |   30 +
 arch/metag/include/asm/thread_info.h               |  155 +++
 arch/metag/include/asm/tlb.h                       |   36 +
 arch/metag/include/asm/tlbflush.h                  |   77 ++
 arch/metag/include/asm/topology.h                  |   53 +
 arch/metag/include/asm/traps.h                     |   48 +
 arch/metag/include/asm/uaccess.h                   |  241 ++++
 arch/metag/include/asm/unistd.h                    |   12 +
 arch/metag/include/asm/user_gateway.h              |   44 +
 arch/metag/include/uapi/asm/Kbuild                 |   13 +
 arch/metag/include/uapi/asm/byteorder.h            |    1 +
 arch/metag/include/uapi/asm/ptrace.h               |  113 ++
 arch/metag/include/uapi/asm/resource.h             |    7 +
 arch/metag/include/uapi/asm/sigcontext.h           |   31 +
 arch/metag/include/uapi/asm/siginfo.h              |    8 +
 arch/metag/include/uapi/asm/swab.h                 |   26 +
 arch/metag/include/uapi/asm/unistd.h               |   21 +
 arch/metag/kernel/.gitignore                       |    1 +
 arch/metag/kernel/Makefile                         |   39 +
 arch/metag/kernel/asm-offsets.c                    |   14 +
 arch/metag/kernel/cachepart.c                      |  124 ++
 arch/metag/kernel/clock.c                          |   53 +
 arch/metag/kernel/core_reg.c                       |  117 ++
 arch/metag/kernel/da.c                             |   23 +
 arch/metag/kernel/devtree.c                        |  100 ++
 arch/metag/kernel/dma.c                            |  507 +++++++
 arch/metag/kernel/ftrace.c                         |  126 ++
 arch/metag/kernel/ftrace_stub.S                    |   76 ++
 arch/metag/kernel/head.S                           |   57 +
 arch/metag/kernel/irq.c                            |  323 +++++
 arch/metag/kernel/kick.c                           |   98 ++
 arch/metag/kernel/machines.c                       |   20 +
 arch/metag/kernel/metag_ksyms.c                    |   80 ++
 arch/metag/kernel/module.c                         |  284 ++++
 arch/metag/kernel/perf/Makefile                    |    3 +
 arch/metag/kernel/perf/perf_event.c                |  861 ++++++++++++
 arch/metag/kernel/perf/perf_event.h                |  106 ++
 arch/metag/kernel/perf_callchain.c                 |   96 ++
 arch/metag/kernel/process.c                        |  461 +++++++
 arch/metag/kernel/ptrace.c                         |  380 ++++++
 arch/metag/kernel/setup.c                          |  571 ++++++++
 arch/metag/kernel/signal.c                         |  344 +++++
 arch/metag/kernel/smp.c                            |  575 ++++++++
 arch/metag/kernel/stacktrace.c                     |  187 +++
 arch/metag/kernel/sys_metag.c                      |  180 +++
 arch/metag/kernel/tbiunexp.S                       |   22 +
 arch/metag/kernel/tcm.c                            |  151 +++
 arch/metag/kernel/time.c                           |   15 +
 arch/metag/kernel/topology.c                       |   77 ++
 arch/metag/kernel/traps.c                          |  978 ++++++++++++++
 arch/metag/kernel/user_gateway.S                   |   97 ++
 arch/metag/kernel/vmlinux.lds.S                    |   71 +
 arch/metag/lib/Makefile                            |   22 +
 arch/metag/lib/ashldi3.S                           |   33 +
 arch/metag/lib/ashrdi3.S                           |   33 +
 arch/metag/lib/checksum.c                          |  168 +++
 arch/metag/lib/clear_page.S                        |   17 +
 arch/metag/lib/cmpdi2.S                            |   32 +
 arch/metag/lib/copy_page.S                         |   20 +
 arch/metag/lib/delay.c                             |   56 +
 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/ucmpdi2.S                           |   27 +
 arch/metag/lib/usercopy.c                          | 1341 ++++++++++++++++++
 arch/metag/mm/Kconfig                              |  153 +++
 arch/metag/mm/Makefile                             |   19 +
 arch/metag/mm/cache.c                              |  441 ++++++
 arch/metag/mm/extable.c                            |   15 +
 arch/metag/mm/fault.c                              |  239 ++++
 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                            |  192 +++
 arch/metag/mm/maccess.c                            |   68 +
 arch/metag/mm/mmu-meta1.c                          |  157 +++
 arch/metag/mm/mmu-meta2.c                          |  207 +++
 arch/metag/mm/numa.c                               |   81 ++
 arch/metag/tbx/Makefile                            |   21 +
 arch/metag/tbx/tbicore.S                           |  136 ++
 arch/metag/tbx/tbictx.S                            |  366 +++++
 arch/metag/tbx/tbictxfpu.S                         |  190 +++
 arch/metag/tbx/tbidefr.S                           |  175 +++
 arch/metag/tbx/tbidspram.S                         |  161 +++
 arch/metag/tbx/tbilogf.S                           |   48 +
 arch/metag/tbx/tbipcx.S                            |  451 +++++++
 arch/metag/tbx/tbiroot.S                           |   87 ++
 arch/metag/tbx/tbisoft.S                           |  237 ++++
 arch/metag/tbx/tbistring.c                         |  114 ++
 arch/metag/tbx/tbitimer.S                          |  207 +++
 drivers/clocksource/Kconfig                        |    5 +
 drivers/clocksource/Makefile                       |    1 +
 drivers/clocksource/metag_generic.c                |  198 +++
 drivers/irqchip/Makefile                           |    2 +
 drivers/irqchip/irq-metag-ext.c                    |  868 ++++++++++++
 drivers/irqchip/irq-metag.c                        |  343 +++++
 drivers/tty/Kconfig                                |   13 +
 drivers/tty/Makefile                               |    1 +
 drivers/tty/metag_da.c                             |  679 ++++++++++
 fs/Kconfig                                         |    1 +
 fs/Makefile                                        |    1 +
 fs/binfmt_elf.c                                    |    4 +
 fs/imgdafs/Kconfig                                 |    6 +
 fs/imgdafs/Makefile                                |    7 +
 fs/imgdafs/imgdafs.h                               |   80 ++
 fs/imgdafs/inode.c                                 |  843 ++++++++++++
 include/asm-generic/io.h                           |    2 +
 include/asm-generic/unistd.h                       |    9 +-
 include/clocksource/metag_generic.h                |   21 +
 include/linux/irqchip/metag-ext.h                  |   33 +
 include/linux/irqchip/metag.h                      |   24 +
 include/linux/mm.h                                 |    2 +
 include/uapi/linux/elf.h                           |    2 +
 kernel/trace/ring_buffer.c                         |    6 +-
 lib/Kconfig.debug                                  |    4 +-
 scripts/checkstack.pl                              |    8 +-
 scripts/recordmcount.c                             |   13 +
 207 files changed, 28428 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/metag/meta-intc.txt
 create mode 100644 Documentation/metag/00-INDEX
 create mode 100644 Documentation/metag/kernel-ABI.txt
 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/dts/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/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_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/global_lock.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/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/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/mach/arch.h
 create mode 100644 arch/metag/include/asm/metag_isa.h
 create mode 100644 arch/metag/include/asm/metag_mem.h
 create mode 100644 arch/metag/include/asm/metag_regs.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/setup.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/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.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/include/uapi/asm/Kbuild
 create mode 100644 arch/metag/include/uapi/asm/byteorder.h
 create mode 100644 arch/metag/include/uapi/asm/ptrace.h
 create mode 100644 arch/metag/include/uapi/asm/resource.h
 create mode 100644 arch/metag/include/uapi/asm/sigcontext.h
 create mode 100644 arch/metag/include/uapi/asm/siginfo.h
 create mode 100644 arch/metag/include/uapi/asm/swab.h
 create mode 100644 arch/metag/include/uapi/asm/unistd.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/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/irq.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/Makefile
 create mode 100644 arch/metag/kernel/perf/perf_event.c
 create mode 100644 arch/metag/kernel/perf/perf_event.h
 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/cmpdi2.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/ucmpdi2.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/cache.c
 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/tbx/Makefile
 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/clocksource/metag_generic.c
 create mode 100644 drivers/irqchip/irq-metag-ext.c
 create mode 100644 drivers/irqchip/irq-metag.c
 create mode 100644 drivers/tty/metag_da.c
 create mode 100644 fs/imgdafs/Kconfig
 create mode 100644 fs/imgdafs/Makefile
 create mode 100644 fs/imgdafs/imgdafs.h
 create mode 100644 fs/imgdafs/inode.c
 create mode 100644 include/clocksource/metag_generic.h
 create mode 100644 include/linux/irqchip/metag-ext.h
 create mode 100644 include/linux/irqchip/metag.h

-- 
1.7.7.6

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

* [PATCH v4 19/43] metag: Device tree
@ 2013-01-29 14:15   ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch
  Cc: Arnd Bergmann, James Hogan, Grant Likely, Rob Herring,
	devicetree-discuss, Sam Ravnborg

Add device tree files to arch/metag.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: Sam Ravnborg <sam@ravnborg.org>
---
v4:
  * move dtb directory into dts and use common dtb rules (Rob Herring)

 arch/metag/boot/dts/Makefile      |   16 ++++++
 arch/metag/boot/dts/skeleton.dts  |   10 ++++
 arch/metag/boot/dts/skeleton.dtsi |   14 +++++
 arch/metag/include/asm/prom.h     |   23 +++++++++
 arch/metag/kernel/devtree.c       |   97 +++++++++++++++++++++++++++++++++++++
 5 files changed, 160 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/boot/dts/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/boot/dts/Makefile b/arch/metag/boot/dts/Makefile
new file mode 100644
index 0000000..e0b5afd
--- /dev/null
+++ b/arch/metag/boot/dts/Makefile
@@ -0,0 +1,16 @@
+dtb-y	+= skeleton.dtb
+
+# Built-in dtb
+builtindtb-y				:= skeleton
+
+ifneq ($(CONFIG_METAG_BUILTIN_DTB_NAME),"")
+	builtindtb-y			:= $(CONFIG_METAG_BUILTIN_DTB_NAME)
+endif
+obj-$(CONFIG_METAG_BUILTIN_DTB)	+= $(patsubst "%",%,$(builtindtb-y)).dtb.o
+
+targets	+= dtbs
+targets	+= $(dtb-y)
+
+dtbs: $(addprefix $(obj)/, $(dtb-y))
+
+clean-files += *.dtb
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/devtree.c b/arch/metag/kernel/devtree.c
new file mode 100644
index 0000000..5b6b1d85
--- /dev/null
+++ b/arch/metag/kernel/devtree.c
@@ -0,0 +1,97 @@
+/*
+ *  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/types.h>
+#include <linux/bootmem.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_fdt.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] 15+ messages in thread

* [PATCH v4 19/43] metag: Device tree
@ 2013-01-29 14:15   ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arch-u79uwXL29TY76Z2rM5mHXA
  Cc: James Hogan, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	Rob Herring, Sam Ravnborg

Add device tree files to arch/metag.

Signed-off-by: James Hogan <james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
Cc: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
Cc: Rob Herring <rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>
Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
Cc: Sam Ravnborg <sam-uyr5N9Q2VtJg9hUCZPvPmw@public.gmane.org>
---
v4:
  * move dtb directory into dts and use common dtb rules (Rob Herring)

 arch/metag/boot/dts/Makefile      |   16 ++++++
 arch/metag/boot/dts/skeleton.dts  |   10 ++++
 arch/metag/boot/dts/skeleton.dtsi |   14 +++++
 arch/metag/include/asm/prom.h     |   23 +++++++++
 arch/metag/kernel/devtree.c       |   97 +++++++++++++++++++++++++++++++++++++
 5 files changed, 160 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/boot/dts/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/boot/dts/Makefile b/arch/metag/boot/dts/Makefile
new file mode 100644
index 0000000..e0b5afd
--- /dev/null
+++ b/arch/metag/boot/dts/Makefile
@@ -0,0 +1,16 @@
+dtb-y	+= skeleton.dtb
+
+# Built-in dtb
+builtindtb-y				:= skeleton
+
+ifneq ($(CONFIG_METAG_BUILTIN_DTB_NAME),"")
+	builtindtb-y			:= $(CONFIG_METAG_BUILTIN_DTB_NAME)
+endif
+obj-$(CONFIG_METAG_BUILTIN_DTB)	+= $(patsubst "%",%,$(builtindtb-y)).dtb.o
+
+targets	+= dtbs
+targets	+= $(dtb-y)
+
+dtbs: $(addprefix $(obj)/, $(dtb-y))
+
+clean-files += *.dtb
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-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
+ *
+ * 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/devtree.c b/arch/metag/kernel/devtree.c
new file mode 100644
index 0000000..5b6b1d85
--- /dev/null
+++ b/arch/metag/kernel/devtree.c
@@ -0,0 +1,97 @@
+/*
+ *  linux/arch/metag/kernel/devtree.c
+ *
+ *  Copyright (C) 2012 Imagination Technologies Ltd.
+ *
+ *  Based on ARM version:
+ *  Copyright (C) 2009 Canonical Ltd. <jeremy.kerr-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
+ *
+ * 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/types.h>
+#include <linux/bootmem.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_fdt.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] 15+ messages in thread

* [PATCH v4 19/43] metag: Device tree
@ 2013-01-29 14:15   ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch
  Cc: Arnd Bergmann, James Hogan, Grant Likely, Rob Herring,
	devicetree-discuss, Sam Ravnborg

Add device tree files to arch/metag.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: Sam Ravnborg <sam@ravnborg.org>
---
v4:
  * move dtb directory into dts and use common dtb rules (Rob Herring)

 arch/metag/boot/dts/Makefile      |   16 ++++++
 arch/metag/boot/dts/skeleton.dts  |   10 ++++
 arch/metag/boot/dts/skeleton.dtsi |   14 +++++
 arch/metag/include/asm/prom.h     |   23 +++++++++
 arch/metag/kernel/devtree.c       |   97 +++++++++++++++++++++++++++++++++++++
 5 files changed, 160 insertions(+), 0 deletions(-)
 create mode 100644 arch/metag/boot/dts/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/boot/dts/Makefile b/arch/metag/boot/dts/Makefile
new file mode 100644
index 0000000..e0b5afd
--- /dev/null
+++ b/arch/metag/boot/dts/Makefile
@@ -0,0 +1,16 @@
+dtb-y	+= skeleton.dtb
+
+# Built-in dtb
+builtindtb-y				:= skeleton
+
+ifneq ($(CONFIG_METAG_BUILTIN_DTB_NAME),"")
+	builtindtb-y			:= $(CONFIG_METAG_BUILTIN_DTB_NAME)
+endif
+obj-$(CONFIG_METAG_BUILTIN_DTB)	+= $(patsubst "%",%,$(builtindtb-y)).dtb.o
+
+targets	+= dtbs
+targets	+= $(dtb-y)
+
+dtbs: $(addprefix $(obj)/, $(dtb-y))
+
+clean-files += *.dtb
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/devtree.c b/arch/metag/kernel/devtree.c
new file mode 100644
index 0000000..5b6b1d85
--- /dev/null
+++ b/arch/metag/kernel/devtree.c
@@ -0,0 +1,97 @@
+/*
+ *  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/types.h>
+#include <linux/bootmem.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_fdt.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] 15+ messages in thread

* [PATCH v4 21/43] metag: Time keeping
  2013-01-29 14:15 ` James Hogan
@ 2013-01-29 14:15   ` James Hogan
  -1 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch
  Cc: Arnd Bergmann, James Hogan, John Stultz, Thomas Gleixner

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>
Cc: John Stultz <johnstul@us.ibm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
---
v4:
  * move to drivers/clocksource/metag_generic.c (Arnd)
  * use cpu notifier to setup secondary cpu timers

 MAINTAINERS                         |    1 +
 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            |   15 +++
 drivers/clocksource/Kconfig         |    5 +
 drivers/clocksource/Makefile        |    1 +
 drivers/clocksource/metag_generic.c |  198 +++++++++++++++++++++++++++++++++++
 include/clocksource/metag_generic.h |   21 ++++
 10 files changed, 378 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
 create mode 100644 drivers/clocksource/metag_generic.c
 create mode 100644 include/clocksource/metag_generic.h

diff --git a/MAINTAINERS b/MAINTAINERS
index fc1c3fe..e99e580 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5040,6 +5040,7 @@ S:	Supported
 F:	arch/metag/
 F:	Documentation/metag/
 F:	Documentation/devicetree/bindings/metag/
+F:	drivers/clocksource/metag_generic.c
 
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
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 6845d80..12c5664 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.
  *
  * @nr_irqs:		Maximum number of IRQs.
  *			If 0, defaults to NR_IRQS in asm-generic/irq.h.
@@ -37,6 +40,7 @@
 struct machine_desc {
 	const char		*name;
 	const char		**dt_compat;
+	struct meta_clock_desc	*clocks;
 
 	unsigned int		nr_irqs;
 
diff --git a/arch/metag/kernel/clock.c b/arch/metag/kernel/clock.c
new file mode 100644
index 0000000..defc840
--- /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_METAG_META21
+	/*
+	 * Meta 2 cores divide down the core clock for the Meta timers, so we
+	 * can estimate the core clock from the divider.
+	 */
+	return (metag_in32(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..17dc107
--- /dev/null
+++ b/arch/metag/kernel/time.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2005-2013 Imagination Technologies Ltd.
+ *
+ * This file contains the Meta-specific time handling details.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <clocksource/metag_generic.h>
+
+void __init time_init(void)
+{
+	metag_generic_timer_init();
+}
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 7fdcbd3..75bc752 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -58,3 +58,8 @@ config CLKSRC_ARM_GENERIC
 	def_bool y if ARM64
 	help
 	  This option enables support for the ARM generic timer.
+
+config CLKSRC_METAG_GENERIC
+	def_bool y if METAG
+	help
+	  This option enables support for the Meta per-thread timers.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index f93453d..09dcd49 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
 obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o
 
 obj-$(CONFIG_CLKSRC_ARM_GENERIC)	+= arm_generic.o
+obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o
diff --git a/drivers/clocksource/metag_generic.c b/drivers/clocksource/metag_generic.c
new file mode 100644
index 0000000..ade7513
--- /dev/null
+++ b/drivers/clocksource/metag_generic.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2005-2013 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Support for Meta per-thread timers.
+ *
+ * Meta hardware threads have 2 timers. The background timer (TXTIMER) is used
+ * as a free-running time base (hz clocksource), and the interrupt timer
+ * (TXTIMERI) is used for the timer interrupt (clock event). Both counters
+ * traditionally count at approximately 1MHz.
+ */
+
+#include <clocksource/metag_generic.h>
+#include <linux/cpu.h>
+#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>
+#include <asm/hwthread.h>
+#include <asm/core_reg.h>
+#include <asm/metag_mem.h>
+#include <asm/tbx.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)
+{
+	__core_reg_set(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 cycle_t metag_clocksource_read(struct clocksource *cs)
+{
+	return __core_reg_get(TXTIMER);
+}
+
+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 = __core_reg_get(TXTIMER);
+	return ticks << HARDWARE_TO_NS_SHIFT;
+}
+
+static void __cpuinit arch_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 = __core_reg_get(TXDIVTIME);
+
+	txdivtime &= ~TXDIVTIME_DIV_BITS;
+	txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS);
+
+	__core_reg_set(TXDIVTIME, txdivtime);
+
+	sprintf(name, "META %d", cpu);
+	clk->name = name;
+	clk->features = CLOCK_EVT_FEAT_ONESHOT,
+
+	clk->rating = 200,
+	clk->shift = 12,
+	clk->irq = tbisig_map(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);
+		__core_reg_set(TXTIMER, val);
+	}
+}
+
+static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
+					   unsigned long action, void *hcpu)
+{
+	int cpu = (long)hcpu;
+
+	switch (action) {
+	case CPU_STARTING:
+	case CPU_STARTING_FROZEN:
+		arch_timer_setup(cpu);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
+	.notifier_call = arch_timer_cpu_notify,
+};
+
+int __init metag_generic_timer_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_METAG_META21
+	hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1);
+#endif
+	clocksource_register_hz(&clocksource_metag, hwtimer_freq);
+
+	setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq);
+
+	/* Configure timer on boot CPU */
+	arch_timer_setup(smp_processor_id());
+
+	/* Hook cpu boot to configure other CPU's timers */
+	register_cpu_notifier(&arch_timer_cpu_nb);
+
+	return 0;
+}
diff --git a/include/clocksource/metag_generic.h b/include/clocksource/metag_generic.h
new file mode 100644
index 0000000..ac17e7d
--- /dev/null
+++ b/include/clocksource/metag_generic.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013 Imaginaton 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __CLKSOURCE_METAG_GENERIC_H
+#define __CLKSOURCE_METAG_GENERIC_H
+
+extern int metag_generic_timer_init(void);
+
+#endif /* __CLKSOURCE_METAG_GENERIC_H */
-- 
1.7.7.6



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

* [PATCH v4 21/43] metag: Time keeping
@ 2013-01-29 14:15   ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch
  Cc: Arnd Bergmann, James Hogan, John Stultz, Thomas Gleixner

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>
Cc: John Stultz <johnstul@us.ibm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
---
v4:
  * move to drivers/clocksource/metag_generic.c (Arnd)
  * use cpu notifier to setup secondary cpu timers

 MAINTAINERS                         |    1 +
 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            |   15 +++
 drivers/clocksource/Kconfig         |    5 +
 drivers/clocksource/Makefile        |    1 +
 drivers/clocksource/metag_generic.c |  198 +++++++++++++++++++++++++++++++++++
 include/clocksource/metag_generic.h |   21 ++++
 10 files changed, 378 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
 create mode 100644 drivers/clocksource/metag_generic.c
 create mode 100644 include/clocksource/metag_generic.h

diff --git a/MAINTAINERS b/MAINTAINERS
index fc1c3fe..e99e580 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5040,6 +5040,7 @@ S:	Supported
 F:	arch/metag/
 F:	Documentation/metag/
 F:	Documentation/devicetree/bindings/metag/
+F:	drivers/clocksource/metag_generic.c
 
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
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 6845d80..12c5664 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.
  *
  * @nr_irqs:		Maximum number of IRQs.
  *			If 0, defaults to NR_IRQS in asm-generic/irq.h.
@@ -37,6 +40,7 @@
 struct machine_desc {
 	const char		*name;
 	const char		**dt_compat;
+	struct meta_clock_desc	*clocks;
 
 	unsigned int		nr_irqs;
 
diff --git a/arch/metag/kernel/clock.c b/arch/metag/kernel/clock.c
new file mode 100644
index 0000000..defc840
--- /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_METAG_META21
+	/*
+	 * Meta 2 cores divide down the core clock for the Meta timers, so we
+	 * can estimate the core clock from the divider.
+	 */
+	return (metag_in32(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..17dc107
--- /dev/null
+++ b/arch/metag/kernel/time.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2005-2013 Imagination Technologies Ltd.
+ *
+ * This file contains the Meta-specific time handling details.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <clocksource/metag_generic.h>
+
+void __init time_init(void)
+{
+	metag_generic_timer_init();
+}
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 7fdcbd3..75bc752 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -58,3 +58,8 @@ config CLKSRC_ARM_GENERIC
 	def_bool y if ARM64
 	help
 	  This option enables support for the ARM generic timer.
+
+config CLKSRC_METAG_GENERIC
+	def_bool y if METAG
+	help
+	  This option enables support for the Meta per-thread timers.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index f93453d..09dcd49 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
 obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o
 
 obj-$(CONFIG_CLKSRC_ARM_GENERIC)	+= arm_generic.o
+obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o
diff --git a/drivers/clocksource/metag_generic.c b/drivers/clocksource/metag_generic.c
new file mode 100644
index 0000000..ade7513
--- /dev/null
+++ b/drivers/clocksource/metag_generic.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2005-2013 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Support for Meta per-thread timers.
+ *
+ * Meta hardware threads have 2 timers. The background timer (TXTIMER) is used
+ * as a free-running time base (hz clocksource), and the interrupt timer
+ * (TXTIMERI) is used for the timer interrupt (clock event). Both counters
+ * traditionally count at approximately 1MHz.
+ */
+
+#include <clocksource/metag_generic.h>
+#include <linux/cpu.h>
+#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>
+#include <asm/hwthread.h>
+#include <asm/core_reg.h>
+#include <asm/metag_mem.h>
+#include <asm/tbx.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)
+{
+	__core_reg_set(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 cycle_t metag_clocksource_read(struct clocksource *cs)
+{
+	return __core_reg_get(TXTIMER);
+}
+
+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 = __core_reg_get(TXTIMER);
+	return ticks << HARDWARE_TO_NS_SHIFT;
+}
+
+static void __cpuinit arch_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 = __core_reg_get(TXDIVTIME);
+
+	txdivtime &= ~TXDIVTIME_DIV_BITS;
+	txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS);
+
+	__core_reg_set(TXDIVTIME, txdivtime);
+
+	sprintf(name, "META %d", cpu);
+	clk->name = name;
+	clk->features = CLOCK_EVT_FEAT_ONESHOT,
+
+	clk->rating = 200,
+	clk->shift = 12,
+	clk->irq = tbisig_map(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);
+		__core_reg_set(TXTIMER, val);
+	}
+}
+
+static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
+					   unsigned long action, void *hcpu)
+{
+	int cpu = (long)hcpu;
+
+	switch (action) {
+	case CPU_STARTING:
+	case CPU_STARTING_FROZEN:
+		arch_timer_setup(cpu);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
+	.notifier_call = arch_timer_cpu_notify,
+};
+
+int __init metag_generic_timer_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_METAG_META21
+	hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1);
+#endif
+	clocksource_register_hz(&clocksource_metag, hwtimer_freq);
+
+	setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq);
+
+	/* Configure timer on boot CPU */
+	arch_timer_setup(smp_processor_id());
+
+	/* Hook cpu boot to configure other CPU's timers */
+	register_cpu_notifier(&arch_timer_cpu_nb);
+
+	return 0;
+}
diff --git a/include/clocksource/metag_generic.h b/include/clocksource/metag_generic.h
new file mode 100644
index 0000000..ac17e7d
--- /dev/null
+++ b/include/clocksource/metag_generic.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013 Imaginaton 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __CLKSOURCE_METAG_GENERIC_H
+#define __CLKSOURCE_METAG_GENERIC_H
+
+extern int metag_generic_timer_init(void);
+
+#endif /* __CLKSOURCE_METAG_GENERIC_H */
-- 
1.7.7.6

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

* [PATCH v4 26/43] metag: Scheduling/Process management
  2013-01-29 14:15 ` James Hogan
@ 2013-01-29 14:15   ` James Hogan
  -1 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch; +Cc: Arnd Bergmann, James Hogan

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
v4:
  * fix idle race (requires use of inline asm to trigger irq entry
    rather than SWITCH instruction) (Arnd)

 arch/metag/include/asm/thread_info.h |  155 ++++++++++++
 arch/metag/kernel/process.c          |  461 ++++++++++++++++++++++++++++++++++
 2 files changed, 616 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..0ecd34d
--- /dev/null
+++ b/arch/metag/include/asm/thread_info.h
@@ -0,0 +1,155 @@
+/* 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
+
+#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))
+
+#define tsk_is_polling(t) test_tsk_thread_flag(t, TIF_POLLING_NRFLAG)
+
+#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..c6efe62
--- /dev/null
+++ b/arch/metag/kernel/process.c
@@ -0,0 +1,461 @@
+/*
+ * 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/syscalls.h>
+#include <linux/uaccess.h>
+#include <asm/core_reg.h>
+#include <asm/user_gateway.h>
+#include <asm/tcm.h>
+#include <asm/traps.h>
+#include <asm/switch_to.h>
+
+/*
+ * Wait for the next interrupt and enable local interrupts
+ */
+static inline void arch_idle(void)
+{
+	int tmp;
+
+	/*
+	 * Quickly jump straight into the interrupt entry point without actually
+	 * triggering an interrupt. When TXSTATI gets read the processor will
+	 * block until an interrupt is triggered.
+	 */
+	asm volatile (/* Switch into ISTAT mode */
+		      "RTH\n\t"
+		      /* Enable local interrupts */
+		      "MOV	TXMASKI, %1\n\t"
+		      /*
+		       * We can't directly "SWAP PC, PCX", so we swap via a
+		       * temporary. Essentially we do:
+		       *  PCX_new = 1f (the place to continue execution)
+		       *  PC = PCX_old
+		       */
+		      "ADD	%0, CPC0, #(1f-.)\n\t"
+		      "SWAP	PCX, %0\n\t"
+		      "MOV	PC, %0\n"
+		      /* Continue execution here with interrupts enabled */
+		      "1:"
+		      : "=a" (tmp)
+		      : "r" (get_trigger_mask()));
+}
+
+void cpu_idle(void)
+{
+	set_thread_flag(TIF_POLLING_NRFLAG);
+
+	while (1) {
+		tick_nohz_idle_enter();
+		rcu_idle_enter();
+
+		while (!need_resched()) {
+			/*
+			 * We need to disable interrupts here to ensure we don't
+			 * miss a wakeup call.
+			 */
+			local_irq_disable();
+			if (!need_resched()) {
+#ifdef CONFIG_HOTPLUG_CPU
+				if (cpu_is_offline(smp_processor_id()))
+					cpu_die();
+#endif
+				arch_idle();
+			} else {
+				local_irq_enable();
+			}
+		}
+
+		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);
+}
+
+int copy_thread(unsigned long clone_flags, unsigned long usp,
+		unsigned long arg, struct task_struct *tsk)
+{
+	struct pt_regs *childregs = task_pt_regs(tsk);
+	void *kernel_context = ((void *) childregs +
+				sizeof(struct pt_regs));
+	unsigned long global_base;
+
+	BUG_ON(((unsigned long)childregs) & 0x7);
+	BUG_ON(((unsigned long)kernel_context) & 0x7);
+
+	memset(&tsk->thread.kernel_context, 0,
+			sizeof(tsk->thread.kernel_context));
+
+	tsk->thread.kernel_context = __TBISwitchInit(kernel_context,
+						     ret_from_fork,
+						     0, 0);
+
+	if (unlikely(tsk->flags & PF_KTHREAD)) {
+		/*
+		 * Make sure we don't leak any kernel data to child's regs
+		 * if kernel thread becomes a userspace thread in the future
+		 */
+		memset(childregs, 0 , sizeof(struct pt_regs));
+
+		global_base = __core_reg_get(A1GbP);
+		childregs->ctx.AX[0].U1 = (unsigned long) global_base;
+		childregs->ctx.AX[0].U0 = (unsigned long) kernel_context;
+		/* Set D1Ar1=arg and D1RtP=usp (fn) */
+		childregs->ctx.DX[4].U1 = usp;
+		childregs->ctx.DX[3].U1 = arg;
+		tsk->thread.int_depth = 2;
+		return 0;
+	}
+	/*
+	 * 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 = *current_pt_regs();
+	/* Set the correct stack for the clone mode */
+	if (usp)
+		childregs->ctx.AX[0].U0 = ALIGN(usp, 8);
+	tsk->thread.int_depth = 1;
+
+	/* set return value for child process */
+	childregs->ctx.DX[0].U0 = 0;
+
+	/* The TLS pointer is passed as an argument to sys_clone. */
+	if (clone_flags & CLONE_SETTLS)
+		tsk->thread.tls_ptr =
+				(__force void __user *)childregs->ctx.DX[1].U1;
+
+#ifdef CONFIG_METAG_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_METAG_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_METAG_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_METAG_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;
+	}
+
+	__core_reg_set(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_METAG_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
+
+
+	from = __TBISwitch(to, &prev->thread.kernel_context);
+
+	/* Restore TLS pointer for this process. */
+	set_gateway_tls(current->thread.tls_ptr);
+
+	return (struct task_struct *) from.Switch.pPara;
+}
+
+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_METAG_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] 15+ messages in thread

* [PATCH v4 26/43] metag: Scheduling/Process management
@ 2013-01-29 14:15   ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch; +Cc: Arnd Bergmann, James Hogan

Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
v4:
  * fix idle race (requires use of inline asm to trigger irq entry
    rather than SWITCH instruction) (Arnd)

 arch/metag/include/asm/thread_info.h |  155 ++++++++++++
 arch/metag/kernel/process.c          |  461 ++++++++++++++++++++++++++++++++++
 2 files changed, 616 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..0ecd34d
--- /dev/null
+++ b/arch/metag/include/asm/thread_info.h
@@ -0,0 +1,155 @@
+/* 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
+
+#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))
+
+#define tsk_is_polling(t) test_tsk_thread_flag(t, TIF_POLLING_NRFLAG)
+
+#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..c6efe62
--- /dev/null
+++ b/arch/metag/kernel/process.c
@@ -0,0 +1,461 @@
+/*
+ * 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/syscalls.h>
+#include <linux/uaccess.h>
+#include <asm/core_reg.h>
+#include <asm/user_gateway.h>
+#include <asm/tcm.h>
+#include <asm/traps.h>
+#include <asm/switch_to.h>
+
+/*
+ * Wait for the next interrupt and enable local interrupts
+ */
+static inline void arch_idle(void)
+{
+	int tmp;
+
+	/*
+	 * Quickly jump straight into the interrupt entry point without actually
+	 * triggering an interrupt. When TXSTATI gets read the processor will
+	 * block until an interrupt is triggered.
+	 */
+	asm volatile (/* Switch into ISTAT mode */
+		      "RTH\n\t"
+		      /* Enable local interrupts */
+		      "MOV	TXMASKI, %1\n\t"
+		      /*
+		       * We can't directly "SWAP PC, PCX", so we swap via a
+		       * temporary. Essentially we do:
+		       *  PCX_new = 1f (the place to continue execution)
+		       *  PC = PCX_old
+		       */
+		      "ADD	%0, CPC0, #(1f-.)\n\t"
+		      "SWAP	PCX, %0\n\t"
+		      "MOV	PC, %0\n"
+		      /* Continue execution here with interrupts enabled */
+		      "1:"
+		      : "=a" (tmp)
+		      : "r" (get_trigger_mask()));
+}
+
+void cpu_idle(void)
+{
+	set_thread_flag(TIF_POLLING_NRFLAG);
+
+	while (1) {
+		tick_nohz_idle_enter();
+		rcu_idle_enter();
+
+		while (!need_resched()) {
+			/*
+			 * We need to disable interrupts here to ensure we don't
+			 * miss a wakeup call.
+			 */
+			local_irq_disable();
+			if (!need_resched()) {
+#ifdef CONFIG_HOTPLUG_CPU
+				if (cpu_is_offline(smp_processor_id()))
+					cpu_die();
+#endif
+				arch_idle();
+			} else {
+				local_irq_enable();
+			}
+		}
+
+		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);
+}
+
+int copy_thread(unsigned long clone_flags, unsigned long usp,
+		unsigned long arg, struct task_struct *tsk)
+{
+	struct pt_regs *childregs = task_pt_regs(tsk);
+	void *kernel_context = ((void *) childregs +
+				sizeof(struct pt_regs));
+	unsigned long global_base;
+
+	BUG_ON(((unsigned long)childregs) & 0x7);
+	BUG_ON(((unsigned long)kernel_context) & 0x7);
+
+	memset(&tsk->thread.kernel_context, 0,
+			sizeof(tsk->thread.kernel_context));
+
+	tsk->thread.kernel_context = __TBISwitchInit(kernel_context,
+						     ret_from_fork,
+						     0, 0);
+
+	if (unlikely(tsk->flags & PF_KTHREAD)) {
+		/*
+		 * Make sure we don't leak any kernel data to child's regs
+		 * if kernel thread becomes a userspace thread in the future
+		 */
+		memset(childregs, 0 , sizeof(struct pt_regs));
+
+		global_base = __core_reg_get(A1GbP);
+		childregs->ctx.AX[0].U1 = (unsigned long) global_base;
+		childregs->ctx.AX[0].U0 = (unsigned long) kernel_context;
+		/* Set D1Ar1=arg and D1RtP=usp (fn) */
+		childregs->ctx.DX[4].U1 = usp;
+		childregs->ctx.DX[3].U1 = arg;
+		tsk->thread.int_depth = 2;
+		return 0;
+	}
+	/*
+	 * 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 = *current_pt_regs();
+	/* Set the correct stack for the clone mode */
+	if (usp)
+		childregs->ctx.AX[0].U0 = ALIGN(usp, 8);
+	tsk->thread.int_depth = 1;
+
+	/* set return value for child process */
+	childregs->ctx.DX[0].U0 = 0;
+
+	/* The TLS pointer is passed as an argument to sys_clone. */
+	if (clone_flags & CLONE_SETTLS)
+		tsk->thread.tls_ptr =
+				(__force void __user *)childregs->ctx.DX[1].U1;
+
+#ifdef CONFIG_METAG_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_METAG_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_METAG_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_METAG_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;
+	}
+
+	__core_reg_set(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_METAG_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
+
+
+	from = __TBISwitch(to, &prev->thread.kernel_context);
+
+	/* Restore TLS pointer for this process. */
+	set_gateway_tls(current->thread.tls_ptr);
+
+	return (struct task_struct *) from.Switch.pPara;
+}
+
+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_METAG_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] 15+ messages in thread

* [PATCH v4 42/43] tty/metag_da: Add metag DA TTY driver
  2013-01-29 14:15 ` James Hogan
@ 2013-01-29 14:15   ` James Hogan
  -1 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch
  Cc: Arnd Bergmann, James Hogan, Alan Cox, Greg Kroah-Hartman,
	Jiri Slaby, Andrew Morton, Mauro Carvalho Chehab,
	Cesar Eduardo Barros, Joe Perches, David S. Miller

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: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
Cc: Cesar Eduardo Barros <cesarb@cesarb.net>
Cc: Joe Perches <joe@perches.com>
Cc: "David S. Miller" <davem@davemloft.net>
---
v4:
  * addressed review comments (Jiri Slaby)

v3:
  * mostly rewritten to address Alan Cox' feedback
  * use del_timer_sync() where appropriate (Alan Cox)
  * convert to use tty_port helpers (Alan Cox)
  * fix tty kref safety (Alan Cox)
  * implement proper output buffering (Alan Cox)
  * fix init error handling
  * implement hangup (Alan Cox)
  * omit specific baud rate (Alan Cox)

v2:
  * update DA TTY to be more thread safe and use tty_ports

 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                 |  679 ++++++++++++++++++++++++++++++++
 9 files changed, 715 insertions(+), 1 deletions(-)
 create mode 100644 drivers/tty/metag_da.c

diff --git a/MAINTAINERS b/MAINTAINERS
index eafc72d..cb89fe3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5043,6 +5043,7 @@ F:	Documentation/devicetree/bindings/metag/
 F:	drivers/clocksource/metag_generic.c
 F:	drivers/irqchip/irq-metag.c
 F:	drivers/irqchip/irq-metag-ext.c
+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 7bf6fee..9cba2f9 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 0773425..ad663ca 100644
--- a/arch/metag/configs/meta1_defconfig
+++ b/arch/metag/configs/meta1_defconfig
@@ -28,6 +28,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 001f9bf..47922e93 100644
--- a/arch/metag/configs/meta2_defconfig
+++ b/arch/metag/configs/meta2_defconfig
@@ -29,6 +29,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 d6f7e46..f508250 100644
--- a/arch/metag/configs/meta2_smp_defconfig
+++ b/arch/metag/configs/meta2_smp_defconfig
@@ -30,6 +30,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 43dcf6a..89f9cdc 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;
 
 /*
@@ -182,6 +187,13 @@ void __init setup_arch(char **cmdline_p)
 	metag_cache_probe();
 
 	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 0ecf22b..5eef53d 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..6a0b2e6
--- /dev/null
+++ b/drivers/tty/metag_da.c
@@ -0,0 +1,679 @@
+/*
+ *  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/atomic.h>
+#include <linux/completion.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/uaccess.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
+
+#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)
+
+/*
+ * A short put delay improves latency but has a high throughput overhead
+ */
+#define DA_TTY_PUT_DELAY (HZ / 100)
+
+static atomic_t num_channels_need_poll = ATOMIC_INIT(0);
+
+static struct timer_list poll_timer;
+
+static struct tty_driver *channel_driver;
+
+static struct timer_list put_timer;
+static struct task_struct *dashtty_thread;
+
+#define RX_BUF_SIZE 1024
+
+enum {
+	INCHR = 1,
+	OUTCHR,
+	RDBUF,
+	WRBUF,
+	RDSTAT
+};
+
+/**
+ * struct dashtty_port - Wrapper struct for dashtty tty_port.
+ * @port:		TTY port data
+ * @rx_lock:		Lock for rx_buf.
+ *			This protects between the poll timer and user context.
+ *			It's also held during read SWITCH operations.
+ * @rx_buf:		Read buffer
+ * @xmit_lock:		Lock for xmit_*, and port.xmit_buf.
+ *			This protects between user context and kernel thread.
+ *			It's also held during write SWITCH operations.
+ * @xmit_cnt:		Size of xmit buffer contents
+ * @xmit_head:		Head of xmit buffer where data is written
+ * @xmit_tail:		Tail of xmit buffer where data is read
+ * @xmit_empty:		Completion for xmit buffer being empty
+ */
+struct dashtty_port {
+	struct tty_port		 port;
+	spinlock_t		 rx_lock;
+	void			*rx_buf;
+	struct mutex		 xmit_lock;
+	unsigned int		 xmit_cnt;
+	unsigned int		 xmit_head;
+	unsigned int		 xmit_tail;
+	struct completion	 xmit_empty;
+};
+
+static struct dashtty_port dashtty_ports[NUM_TTY_CHANNELS];
+
+static atomic_t dashtty_xmit_cnt = ATOMIC_INIT(0);
+static wait_queue_head_t dashtty_waitqueue;
+
+/*
+ * Low-level DA channel access routines
+ */
+static int chancall(int in_bios_function, int in_channel,
+		    int in_arg2, void *in_arg3,
+		    void *in_arg4)
+{
+	register int   bios_function asm("D1Ar1") = in_bios_function;
+	register int   channel       asm("D0Ar2") = in_channel;
+	register int   arg2          asm("D1Ar3") = in_arg2;
+	register void *arg3          asm("D0Ar4") = in_arg3;
+	register void *arg4          asm("D1Ar5") = in_arg4;
+	register int   bios_call     asm("D0Ar6") = 3;
+	register int   result        asm("D0Re0");
+
+	asm volatile (
+		"MSETL	[A0StP++], %6,%4,%2\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"
+		: "=d" (result)   /* outs */
+		: "d" (bios_function),
+		  "d" (channel),
+		  "d" (arg2),
+		  "d" (arg3),
+		  "d" (arg4),
+		  "d" (bios_call) /* ins */
+		: "memory");
+
+	return result;
+}
+
+/*
+ * Attempts to fetch count bytes from channel and returns actual count.
+ */
+static int fetch_data(struct tty_struct *tty)
+{
+	unsigned int channel = tty->index;
+	struct dashtty_port *dport = &dashtty_ports[channel];
+	int received = 0;
+
+	spin_lock_bh(&dport->rx_lock);
+	/* check the port isn't being shut down */
+	if (!dport->rx_buf)
+		goto unlock;
+	if (chancall(RDBUF, channel, RX_BUF_SIZE,
+		     (void *)dport->rx_buf, &received) == CONAOK) {
+		if (received) {
+			int space;
+			unsigned char *cbuf;
+
+			space = tty_prepare_flip_string(tty, &cbuf, received);
+
+			if (space <= 0)
+				goto unlock;
+
+			memcpy(cbuf, dport->rx_buf, space);
+			tty_flip_buffer_push(tty);
+		}
+	}
+unlock:
+	spin_unlock_bh(&dport->rx_lock);
+
+	return received;
+}
+
+/**
+ * find_channel_to_poll() - Returns kref to the next channel tty to poll.
+ * Returns:	The TTY of the next channel to poll, or NULL if no TTY needs
+ *		polling. Release with tty_kref_put().
+ */
+static struct tty_struct *find_channel_to_poll(void)
+{
+	static int last_polled_channel;
+	int last = last_polled_channel;
+	int chan;
+	struct tty_struct *tty = NULL;
+
+	for (chan = last + 1; ; ++chan) {
+		if (chan >= NUM_TTY_CHANNELS)
+			chan = 0;
+
+		tty = tty_port_tty_get(&dashtty_ports[chan].port);
+		if (tty) {
+			last_polled_channel = chan;
+			return tty;
+		}
+
+		if (chan == last)
+			break;
+	}
+	return tty;
+}
+
+/**
+ * put_channel_data() - Write out a block of channel data.
+ * @chan:	DA channel number.
+ *
+ * Write a single block of data out to the debug adapter. If the circular buffer
+ * is wrapped then only the first block is written.
+ *
+ * Returns:	1 if the remote buffer was too full to accept data.
+ *		0 otherwise.
+ */
+static int put_channel_data(unsigned int chan)
+{
+	struct dashtty_port *dport;
+	struct tty_struct *tty;
+	int number_written;
+	unsigned int count = 0;
+
+	dport = &dashtty_ports[chan];
+	mutex_lock(&dport->xmit_lock);
+	if (dport->xmit_cnt) {
+		count = min((unsigned int)(SERIAL_XMIT_SIZE - dport->xmit_tail),
+			    dport->xmit_cnt);
+		chancall(WRBUF, chan, count,
+			 dport->port.xmit_buf + dport->xmit_tail,
+			 &number_written);
+		dport->xmit_cnt -= number_written;
+		if (!dport->xmit_cnt) {
+			/* reset pointers to avoid wraps */
+			dport->xmit_head = 0;
+			dport->xmit_tail = 0;
+			complete(&dport->xmit_empty);
+		} else {
+			dport->xmit_tail += number_written;
+			if (dport->xmit_tail >= SERIAL_XMIT_SIZE)
+				dport->xmit_tail -= SERIAL_XMIT_SIZE;
+		}
+		atomic_sub(number_written, &dashtty_xmit_cnt);
+	}
+	mutex_unlock(&dport->xmit_lock);
+
+	/* if we've made more data available, wake up tty */
+	if (count && number_written) {
+		tty = tty_port_tty_get(&dport->port);
+		if (tty) {
+			tty_wakeup(tty);
+			tty_kref_put(tty);
+		}
+	}
+
+	/* did the write fail? */
+	return count && !number_written;
+}
+
+/**
+ * put_data() - Kernel thread to write out blocks of channel data to DA.
+ * @arg:	Unused.
+ *
+ * This kernel thread runs while @dashtty_xmit_cnt != 0, and loops over the
+ * channels to write out any buffered data. If any of the channels stall due to
+ * the remote buffer being full, a hold off happens to allow the debugger to
+ * drain the buffer.
+ */
+static int put_data(void *arg)
+{
+	unsigned int chan, stall;
+
+	__set_current_state(TASK_RUNNING);
+	while (!kthread_should_stop()) {
+		/*
+		 * For each channel see if there's anything to transmit in the
+		 * port's xmit_buf.
+		 */
+		stall = 0;
+		for (chan = 0; chan < NUM_TTY_CHANNELS; ++chan)
+			stall += put_channel_data(chan);
+
+		/*
+		 * If some of the buffers are full, hold off for a short while
+		 * to allow them to empty.
+		 */
+		if (stall)
+			msleep(25);
+
+		wait_event_interruptible(dashtty_waitqueue,
+					 atomic_read(&dashtty_xmit_cnt));
+	}
+
+	return 0;
+}
+
+/*
+ *	This gets called every DA_TTY_POLL and polls the channels for data
+ */
+static void dashtty_timer(unsigned long ignored)
+{
+	struct tty_struct *tty;
+
+	/* If there are no ports open do nothing and don't poll again. */
+	if (!atomic_read(&num_channels_need_poll))
+		return;
+
+	tty = find_channel_to_poll();
+
+	/* Did we find a channel to poll? */
+	if (tty) {
+		fetch_data(tty);
+		tty_kref_put(tty);
+	}
+
+	mod_timer_pinned(&poll_timer, jiffies + DA_TTY_POLL);
+}
+
+static void add_poll_timer(struct timer_list *poll_timer)
+{
+	setup_timer(poll_timer, dashtty_timer, 0);
+	poll_timer->expires = jiffies + DA_TTY_POLL;
+
+	/*
+	 * Always attach the timer to the boot CPU. The DA channels are per-CPU
+	 * so all polling should be from a single CPU.
+	 */
+	add_timer_on(poll_timer, 0);
+}
+
+static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+	struct dashtty_port *dport = container_of(port, struct dashtty_port,
+						  port);
+	void *rx_buf;
+
+	/* Allocate the buffer we use for writing data */
+	if (tty_port_alloc_xmit_buf(port) < 0)
+		goto err;
+
+	/* Allocate the buffer we use for reading data */
+	rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL);
+	if (!rx_buf)
+		goto err_free_xmit;
+
+	spin_lock_bh(&dport->rx_lock);
+	dport->rx_buf = rx_buf;
+	spin_unlock_bh(&dport->rx_lock);
+
+	/*
+	 * 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 (dport != &dashtty_ports[CONSOLE_CHANNEL])
+		if (atomic_inc_return(&num_channels_need_poll) == 1)
+			add_poll_timer(&poll_timer);
+
+	return 0;
+err_free_xmit:
+	tty_port_free_xmit_buf(port);
+err:
+	return -ENOMEM;
+}
+
+static void dashtty_port_shutdown(struct tty_port *port)
+{
+	struct dashtty_port *dport = container_of(port, struct dashtty_port,
+						  port);
+	void *rx_buf;
+	unsigned int count;
+
+	/* stop reading */
+	if (dport != &dashtty_ports[CONSOLE_CHANNEL])
+		if (atomic_dec_and_test(&num_channels_need_poll))
+			del_timer_sync(&poll_timer);
+
+	mutex_lock(&dport->xmit_lock);
+	count = dport->xmit_cnt;
+	mutex_unlock(&dport->xmit_lock);
+	if (count) {
+		/*
+		 * There's still data to write out, so wake and wait for the
+		 * writer thread to drain the buffer.
+		 */
+		del_timer(&put_timer);
+		wake_up_interruptible(&dashtty_waitqueue);
+		wait_for_completion(&dport->xmit_empty);
+	}
+
+	/* Null the read buffer (timer could still be running!) */
+	spin_lock_bh(&dport->rx_lock);
+	rx_buf = dport->rx_buf;
+	dport->rx_buf = NULL;
+	spin_unlock_bh(&dport->rx_lock);
+	/* Free the read buffer */
+	kfree(rx_buf);
+
+	/* Free the write buffer */
+	tty_port_free_xmit_buf(port);
+}
+
+static const struct tty_port_operations dashtty_port_ops = {
+	.activate	= dashtty_port_activate,
+	.shutdown	= dashtty_port_shutdown,
+};
+
+static int dashtty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	return tty_port_install(&dashtty_ports[tty->index].port, driver, tty);
+}
+
+static int dashtty_open(struct tty_struct *tty, struct file *filp)
+{
+	return tty_port_open(tty->port, tty, filp);
+}
+
+static void dashtty_close(struct tty_struct *tty, struct file *filp)
+{
+	return tty_port_close(tty->port, tty, filp);
+}
+
+static void dashtty_hangup(struct tty_struct *tty)
+{
+	int channel;
+	struct dashtty_port *dport;
+
+	channel = tty->index;
+	dport = &dashtty_ports[channel];
+
+	/* drop any data in the xmit buffer */
+	mutex_lock(&dport->xmit_lock);
+	if (dport->xmit_cnt) {
+		atomic_sub(dport->xmit_cnt, &dashtty_xmit_cnt);
+		dport->xmit_cnt = 0;
+		dport->xmit_head = 0;
+		dport->xmit_tail = 0;
+		complete(&dport->xmit_empty);
+	}
+	mutex_unlock(&dport->xmit_lock);
+
+	tty_port_hangup(tty->port);
+}
+
+/**
+ * dashtty_put_timer() - Delayed wake up of kernel thread.
+ * @ignored:	unused
+ *
+ * This timer function wakes up the kernel thread if any data exists in the
+ * buffers. It is used to delay the expensive writeout until the writer has
+ * stopped writing.
+ */
+static void dashtty_put_timer(unsigned long ignored)
+{
+	if (atomic_read(&dashtty_xmit_cnt))
+		wake_up_interruptible(&dashtty_waitqueue);
+}
+
+static int dashtty_write(struct tty_struct *tty, const unsigned char *buf,
+			 int total)
+{
+	int channel, count, block;
+	struct dashtty_port *dport;
+
+	/* Determine the channel */
+	channel = tty->index;
+	dport = &dashtty_ports[channel];
+
+	/*
+	 * Write to output buffer.
+	 *
+	 * The reason that we asynchronously write the buffer is because if we
+	 * were to write the buffer synchronously then because DA 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.
+	 */
+	mutex_lock(&dport->xmit_lock);
+	/* work out how many bytes we can write to the xmit buffer */
+	total = min(total, (int)(SERIAL_XMIT_SIZE - dport->xmit_cnt));
+	atomic_add(total, &dashtty_xmit_cnt);
+	dport->xmit_cnt += total;
+	/* write the actual bytes (may need splitting if it wraps) */
+	for (count = total; count; count -= block) {
+		block = min(count, (int)(SERIAL_XMIT_SIZE - dport->xmit_head));
+		memcpy(dport->port.xmit_buf + dport->xmit_head, buf, block);
+		dport->xmit_head += block;
+		if (dport->xmit_head >= SERIAL_XMIT_SIZE)
+			dport->xmit_head -= SERIAL_XMIT_SIZE;
+		buf += block;
+	}
+	count = dport->xmit_cnt;
+	/* xmit buffer no longer empty? */
+	if (count)
+		INIT_COMPLETION(dport->xmit_empty);
+	mutex_unlock(&dport->xmit_lock);
+
+	if (total) {
+		/*
+		 * If the buffer is full, wake up the kthread, otherwise allow
+		 * some more time for the buffer to fill up a bit before waking
+		 * it.
+		 */
+		if (count == SERIAL_XMIT_SIZE) {
+			del_timer(&put_timer);
+			wake_up_interruptible(&dashtty_waitqueue);
+		} else {
+			mod_timer(&put_timer, jiffies + DA_TTY_PUT_DELAY);
+		}
+	}
+	return total;
+}
+
+static int dashtty_write_room(struct tty_struct *tty)
+{
+	struct dashtty_port *dport;
+	int channel;
+	int room;
+
+	channel = tty->index;
+	dport = &dashtty_ports[channel];
+
+	/* report the space in the xmit buffer */
+	mutex_lock(&dport->xmit_lock);
+	room = SERIAL_XMIT_SIZE - dport->xmit_cnt;
+	mutex_unlock(&dport->xmit_lock);
+
+	return room;
+}
+
+static int dashtty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct dashtty_port *dport;
+	int channel;
+	int chars;
+
+	channel = tty->index;
+	dport = &dashtty_ports[channel];
+
+	/* report the number of bytes in the xmit buffer */
+	mutex_lock(&dport->xmit_lock);
+	chars = dport->xmit_cnt;
+	mutex_unlock(&dport->xmit_lock);
+
+	return chars;
+}
+
+static const struct tty_operations dashtty_ops = {
+	.install		= dashtty_install,
+	.open			= dashtty_open,
+	.close			= dashtty_close,
+	.hangup			= dashtty_hangup,
+	.write			= dashtty_write,
+	.write_room		= dashtty_write_room,
+	.chars_in_buffer	= dashtty_chars_in_buffer,
+};
+
+static int __init dashtty_init(void)
+{
+	int ret;
+	int nport;
+	struct dashtty_port *dport;
+
+	if (!metag_da_enabled())
+		return -ENODEV;
+
+	channel_driver = tty_alloc_driver(NUM_TTY_CHANNELS,
+					  TTY_DRIVER_REAL_RAW);
+	if (IS_ERR(channel_driver))
+		return PTR_ERR(channel_driver);
+
+	channel_driver->driver_name = "metag_da";
+	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 |= CLOCAL;
+
+	tty_set_operations(channel_driver, &dashtty_ops);
+	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+		dport = &dashtty_ports[nport];
+		tty_port_init(&dport->port);
+		dport->port.ops = &dashtty_port_ops;
+		spin_lock_init(&dport->rx_lock);
+		mutex_init(&dport->xmit_lock);
+		/* the xmit buffer starts empty, i.e. completely written */
+		init_completion(&dport->xmit_empty);
+		complete(&dport->xmit_empty);
+	}
+
+	setup_timer(&put_timer, dashtty_put_timer, 0);
+
+	init_waitqueue_head(&dashtty_waitqueue);
+	dashtty_thread = kthread_create(put_data, NULL, "ttyDA");
+	if (IS_ERR(dashtty_thread)) {
+		pr_err("Couldn't create dashtty thread\n");
+		ret = PTR_ERR(dashtty_thread);
+		goto err_destroy_ports;
+	}
+	/*
+	 * Bind the writer thread to the boot CPU so it can't migrate.
+	 * DA channels are per-CPU and we want all channel I/O to be on a single
+	 * predictable CPU.
+	 */
+	kthread_bind(dashtty_thread, 0);
+	wake_up_process(dashtty_thread);
+
+	ret = tty_register_driver(channel_driver);
+
+	if (ret < 0) {
+		pr_err("Couldn't install dashtty driver: err %d\n",
+		       ret);
+		goto err_stop_kthread;
+	}
+
+	return 0;
+
+err_stop_kthread:
+	kthread_stop(dashtty_thread);
+err_destroy_ports:
+	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+		dport = &dashtty_ports[nport];
+		tty_port_destroy(&dport->port);
+	}
+	put_tty_driver(channel_driver);
+	return ret;
+}
+
+static void dashtty_exit(void)
+{
+	int nport;
+	struct dashtty_port *dport;
+
+	del_timer_sync(&put_timer);
+	kthread_stop(dashtty_thread);
+	del_timer_sync(&poll_timer);
+	tty_unregister_driver(channel_driver);
+	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+		dport = &dashtty_ports[nport];
+		tty_port_destroy(&dport->port);
+	}
+	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] 15+ messages in thread

* [PATCH v4 42/43] tty/metag_da: Add metag DA TTY driver
@ 2013-01-29 14:15   ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch
  Cc: Arnd Bergmann, James Hogan, Alan Cox, Greg Kroah-Hartman,
	Jiri Slaby, Andrew Morton, Mauro Carvalho Chehab,
	Cesar Eduardo Barros, Joe Perches, David S. Miller

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: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
Cc: Cesar Eduardo Barros <cesarb@cesarb.net>
Cc: Joe Perches <joe@perches.com>
Cc: "David S. Miller" <davem@davemloft.net>
---
v4:
  * addressed review comments (Jiri Slaby)

v3:
  * mostly rewritten to address Alan Cox' feedback
  * use del_timer_sync() where appropriate (Alan Cox)
  * convert to use tty_port helpers (Alan Cox)
  * fix tty kref safety (Alan Cox)
  * implement proper output buffering (Alan Cox)
  * fix init error handling
  * implement hangup (Alan Cox)
  * omit specific baud rate (Alan Cox)

v2:
  * update DA TTY to be more thread safe and use tty_ports

 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                 |  679 ++++++++++++++++++++++++++++++++
 9 files changed, 715 insertions(+), 1 deletions(-)
 create mode 100644 drivers/tty/metag_da.c

diff --git a/MAINTAINERS b/MAINTAINERS
index eafc72d..cb89fe3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5043,6 +5043,7 @@ F:	Documentation/devicetree/bindings/metag/
 F:	drivers/clocksource/metag_generic.c
 F:	drivers/irqchip/irq-metag.c
 F:	drivers/irqchip/irq-metag-ext.c
+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 7bf6fee..9cba2f9 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 0773425..ad663ca 100644
--- a/arch/metag/configs/meta1_defconfig
+++ b/arch/metag/configs/meta1_defconfig
@@ -28,6 +28,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 001f9bf..47922e93 100644
--- a/arch/metag/configs/meta2_defconfig
+++ b/arch/metag/configs/meta2_defconfig
@@ -29,6 +29,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 d6f7e46..f508250 100644
--- a/arch/metag/configs/meta2_smp_defconfig
+++ b/arch/metag/configs/meta2_smp_defconfig
@@ -30,6 +30,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 43dcf6a..89f9cdc 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;
 
 /*
@@ -182,6 +187,13 @@ void __init setup_arch(char **cmdline_p)
 	metag_cache_probe();
 
 	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 0ecf22b..5eef53d 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..6a0b2e6
--- /dev/null
+++ b/drivers/tty/metag_da.c
@@ -0,0 +1,679 @@
+/*
+ *  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/atomic.h>
+#include <linux/completion.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/uaccess.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
+
+#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)
+
+/*
+ * A short put delay improves latency but has a high throughput overhead
+ */
+#define DA_TTY_PUT_DELAY (HZ / 100)
+
+static atomic_t num_channels_need_poll = ATOMIC_INIT(0);
+
+static struct timer_list poll_timer;
+
+static struct tty_driver *channel_driver;
+
+static struct timer_list put_timer;
+static struct task_struct *dashtty_thread;
+
+#define RX_BUF_SIZE 1024
+
+enum {
+	INCHR = 1,
+	OUTCHR,
+	RDBUF,
+	WRBUF,
+	RDSTAT
+};
+
+/**
+ * struct dashtty_port - Wrapper struct for dashtty tty_port.
+ * @port:		TTY port data
+ * @rx_lock:		Lock for rx_buf.
+ *			This protects between the poll timer and user context.
+ *			It's also held during read SWITCH operations.
+ * @rx_buf:		Read buffer
+ * @xmit_lock:		Lock for xmit_*, and port.xmit_buf.
+ *			This protects between user context and kernel thread.
+ *			It's also held during write SWITCH operations.
+ * @xmit_cnt:		Size of xmit buffer contents
+ * @xmit_head:		Head of xmit buffer where data is written
+ * @xmit_tail:		Tail of xmit buffer where data is read
+ * @xmit_empty:		Completion for xmit buffer being empty
+ */
+struct dashtty_port {
+	struct tty_port		 port;
+	spinlock_t		 rx_lock;
+	void			*rx_buf;
+	struct mutex		 xmit_lock;
+	unsigned int		 xmit_cnt;
+	unsigned int		 xmit_head;
+	unsigned int		 xmit_tail;
+	struct completion	 xmit_empty;
+};
+
+static struct dashtty_port dashtty_ports[NUM_TTY_CHANNELS];
+
+static atomic_t dashtty_xmit_cnt = ATOMIC_INIT(0);
+static wait_queue_head_t dashtty_waitqueue;
+
+/*
+ * Low-level DA channel access routines
+ */
+static int chancall(int in_bios_function, int in_channel,
+		    int in_arg2, void *in_arg3,
+		    void *in_arg4)
+{
+	register int   bios_function asm("D1Ar1") = in_bios_function;
+	register int   channel       asm("D0Ar2") = in_channel;
+	register int   arg2          asm("D1Ar3") = in_arg2;
+	register void *arg3          asm("D0Ar4") = in_arg3;
+	register void *arg4          asm("D1Ar5") = in_arg4;
+	register int   bios_call     asm("D0Ar6") = 3;
+	register int   result        asm("D0Re0");
+
+	asm volatile (
+		"MSETL	[A0StP++], %6,%4,%2\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"
+		: "=d" (result)   /* outs */
+		: "d" (bios_function),
+		  "d" (channel),
+		  "d" (arg2),
+		  "d" (arg3),
+		  "d" (arg4),
+		  "d" (bios_call) /* ins */
+		: "memory");
+
+	return result;
+}
+
+/*
+ * Attempts to fetch count bytes from channel and returns actual count.
+ */
+static int fetch_data(struct tty_struct *tty)
+{
+	unsigned int channel = tty->index;
+	struct dashtty_port *dport = &dashtty_ports[channel];
+	int received = 0;
+
+	spin_lock_bh(&dport->rx_lock);
+	/* check the port isn't being shut down */
+	if (!dport->rx_buf)
+		goto unlock;
+	if (chancall(RDBUF, channel, RX_BUF_SIZE,
+		     (void *)dport->rx_buf, &received) == CONAOK) {
+		if (received) {
+			int space;
+			unsigned char *cbuf;
+
+			space = tty_prepare_flip_string(tty, &cbuf, received);
+
+			if (space <= 0)
+				goto unlock;
+
+			memcpy(cbuf, dport->rx_buf, space);
+			tty_flip_buffer_push(tty);
+		}
+	}
+unlock:
+	spin_unlock_bh(&dport->rx_lock);
+
+	return received;
+}
+
+/**
+ * find_channel_to_poll() - Returns kref to the next channel tty to poll.
+ * Returns:	The TTY of the next channel to poll, or NULL if no TTY needs
+ *		polling. Release with tty_kref_put().
+ */
+static struct tty_struct *find_channel_to_poll(void)
+{
+	static int last_polled_channel;
+	int last = last_polled_channel;
+	int chan;
+	struct tty_struct *tty = NULL;
+
+	for (chan = last + 1; ; ++chan) {
+		if (chan >= NUM_TTY_CHANNELS)
+			chan = 0;
+
+		tty = tty_port_tty_get(&dashtty_ports[chan].port);
+		if (tty) {
+			last_polled_channel = chan;
+			return tty;
+		}
+
+		if (chan == last)
+			break;
+	}
+	return tty;
+}
+
+/**
+ * put_channel_data() - Write out a block of channel data.
+ * @chan:	DA channel number.
+ *
+ * Write a single block of data out to the debug adapter. If the circular buffer
+ * is wrapped then only the first block is written.
+ *
+ * Returns:	1 if the remote buffer was too full to accept data.
+ *		0 otherwise.
+ */
+static int put_channel_data(unsigned int chan)
+{
+	struct dashtty_port *dport;
+	struct tty_struct *tty;
+	int number_written;
+	unsigned int count = 0;
+
+	dport = &dashtty_ports[chan];
+	mutex_lock(&dport->xmit_lock);
+	if (dport->xmit_cnt) {
+		count = min((unsigned int)(SERIAL_XMIT_SIZE - dport->xmit_tail),
+			    dport->xmit_cnt);
+		chancall(WRBUF, chan, count,
+			 dport->port.xmit_buf + dport->xmit_tail,
+			 &number_written);
+		dport->xmit_cnt -= number_written;
+		if (!dport->xmit_cnt) {
+			/* reset pointers to avoid wraps */
+			dport->xmit_head = 0;
+			dport->xmit_tail = 0;
+			complete(&dport->xmit_empty);
+		} else {
+			dport->xmit_tail += number_written;
+			if (dport->xmit_tail >= SERIAL_XMIT_SIZE)
+				dport->xmit_tail -= SERIAL_XMIT_SIZE;
+		}
+		atomic_sub(number_written, &dashtty_xmit_cnt);
+	}
+	mutex_unlock(&dport->xmit_lock);
+
+	/* if we've made more data available, wake up tty */
+	if (count && number_written) {
+		tty = tty_port_tty_get(&dport->port);
+		if (tty) {
+			tty_wakeup(tty);
+			tty_kref_put(tty);
+		}
+	}
+
+	/* did the write fail? */
+	return count && !number_written;
+}
+
+/**
+ * put_data() - Kernel thread to write out blocks of channel data to DA.
+ * @arg:	Unused.
+ *
+ * This kernel thread runs while @dashtty_xmit_cnt != 0, and loops over the
+ * channels to write out any buffered data. If any of the channels stall due to
+ * the remote buffer being full, a hold off happens to allow the debugger to
+ * drain the buffer.
+ */
+static int put_data(void *arg)
+{
+	unsigned int chan, stall;
+
+	__set_current_state(TASK_RUNNING);
+	while (!kthread_should_stop()) {
+		/*
+		 * For each channel see if there's anything to transmit in the
+		 * port's xmit_buf.
+		 */
+		stall = 0;
+		for (chan = 0; chan < NUM_TTY_CHANNELS; ++chan)
+			stall += put_channel_data(chan);
+
+		/*
+		 * If some of the buffers are full, hold off for a short while
+		 * to allow them to empty.
+		 */
+		if (stall)
+			msleep(25);
+
+		wait_event_interruptible(dashtty_waitqueue,
+					 atomic_read(&dashtty_xmit_cnt));
+	}
+
+	return 0;
+}
+
+/*
+ *	This gets called every DA_TTY_POLL and polls the channels for data
+ */
+static void dashtty_timer(unsigned long ignored)
+{
+	struct tty_struct *tty;
+
+	/* If there are no ports open do nothing and don't poll again. */
+	if (!atomic_read(&num_channels_need_poll))
+		return;
+
+	tty = find_channel_to_poll();
+
+	/* Did we find a channel to poll? */
+	if (tty) {
+		fetch_data(tty);
+		tty_kref_put(tty);
+	}
+
+	mod_timer_pinned(&poll_timer, jiffies + DA_TTY_POLL);
+}
+
+static void add_poll_timer(struct timer_list *poll_timer)
+{
+	setup_timer(poll_timer, dashtty_timer, 0);
+	poll_timer->expires = jiffies + DA_TTY_POLL;
+
+	/*
+	 * Always attach the timer to the boot CPU. The DA channels are per-CPU
+	 * so all polling should be from a single CPU.
+	 */
+	add_timer_on(poll_timer, 0);
+}
+
+static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+	struct dashtty_port *dport = container_of(port, struct dashtty_port,
+						  port);
+	void *rx_buf;
+
+	/* Allocate the buffer we use for writing data */
+	if (tty_port_alloc_xmit_buf(port) < 0)
+		goto err;
+
+	/* Allocate the buffer we use for reading data */
+	rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL);
+	if (!rx_buf)
+		goto err_free_xmit;
+
+	spin_lock_bh(&dport->rx_lock);
+	dport->rx_buf = rx_buf;
+	spin_unlock_bh(&dport->rx_lock);
+
+	/*
+	 * 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 (dport != &dashtty_ports[CONSOLE_CHANNEL])
+		if (atomic_inc_return(&num_channels_need_poll) == 1)
+			add_poll_timer(&poll_timer);
+
+	return 0;
+err_free_xmit:
+	tty_port_free_xmit_buf(port);
+err:
+	return -ENOMEM;
+}
+
+static void dashtty_port_shutdown(struct tty_port *port)
+{
+	struct dashtty_port *dport = container_of(port, struct dashtty_port,
+						  port);
+	void *rx_buf;
+	unsigned int count;
+
+	/* stop reading */
+	if (dport != &dashtty_ports[CONSOLE_CHANNEL])
+		if (atomic_dec_and_test(&num_channels_need_poll))
+			del_timer_sync(&poll_timer);
+
+	mutex_lock(&dport->xmit_lock);
+	count = dport->xmit_cnt;
+	mutex_unlock(&dport->xmit_lock);
+	if (count) {
+		/*
+		 * There's still data to write out, so wake and wait for the
+		 * writer thread to drain the buffer.
+		 */
+		del_timer(&put_timer);
+		wake_up_interruptible(&dashtty_waitqueue);
+		wait_for_completion(&dport->xmit_empty);
+	}
+
+	/* Null the read buffer (timer could still be running!) */
+	spin_lock_bh(&dport->rx_lock);
+	rx_buf = dport->rx_buf;
+	dport->rx_buf = NULL;
+	spin_unlock_bh(&dport->rx_lock);
+	/* Free the read buffer */
+	kfree(rx_buf);
+
+	/* Free the write buffer */
+	tty_port_free_xmit_buf(port);
+}
+
+static const struct tty_port_operations dashtty_port_ops = {
+	.activate	= dashtty_port_activate,
+	.shutdown	= dashtty_port_shutdown,
+};
+
+static int dashtty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	return tty_port_install(&dashtty_ports[tty->index].port, driver, tty);
+}
+
+static int dashtty_open(struct tty_struct *tty, struct file *filp)
+{
+	return tty_port_open(tty->port, tty, filp);
+}
+
+static void dashtty_close(struct tty_struct *tty, struct file *filp)
+{
+	return tty_port_close(tty->port, tty, filp);
+}
+
+static void dashtty_hangup(struct tty_struct *tty)
+{
+	int channel;
+	struct dashtty_port *dport;
+
+	channel = tty->index;
+	dport = &dashtty_ports[channel];
+
+	/* drop any data in the xmit buffer */
+	mutex_lock(&dport->xmit_lock);
+	if (dport->xmit_cnt) {
+		atomic_sub(dport->xmit_cnt, &dashtty_xmit_cnt);
+		dport->xmit_cnt = 0;
+		dport->xmit_head = 0;
+		dport->xmit_tail = 0;
+		complete(&dport->xmit_empty);
+	}
+	mutex_unlock(&dport->xmit_lock);
+
+	tty_port_hangup(tty->port);
+}
+
+/**
+ * dashtty_put_timer() - Delayed wake up of kernel thread.
+ * @ignored:	unused
+ *
+ * This timer function wakes up the kernel thread if any data exists in the
+ * buffers. It is used to delay the expensive writeout until the writer has
+ * stopped writing.
+ */
+static void dashtty_put_timer(unsigned long ignored)
+{
+	if (atomic_read(&dashtty_xmit_cnt))
+		wake_up_interruptible(&dashtty_waitqueue);
+}
+
+static int dashtty_write(struct tty_struct *tty, const unsigned char *buf,
+			 int total)
+{
+	int channel, count, block;
+	struct dashtty_port *dport;
+
+	/* Determine the channel */
+	channel = tty->index;
+	dport = &dashtty_ports[channel];
+
+	/*
+	 * Write to output buffer.
+	 *
+	 * The reason that we asynchronously write the buffer is because if we
+	 * were to write the buffer synchronously then because DA 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.
+	 */
+	mutex_lock(&dport->xmit_lock);
+	/* work out how many bytes we can write to the xmit buffer */
+	total = min(total, (int)(SERIAL_XMIT_SIZE - dport->xmit_cnt));
+	atomic_add(total, &dashtty_xmit_cnt);
+	dport->xmit_cnt += total;
+	/* write the actual bytes (may need splitting if it wraps) */
+	for (count = total; count; count -= block) {
+		block = min(count, (int)(SERIAL_XMIT_SIZE - dport->xmit_head));
+		memcpy(dport->port.xmit_buf + dport->xmit_head, buf, block);
+		dport->xmit_head += block;
+		if (dport->xmit_head >= SERIAL_XMIT_SIZE)
+			dport->xmit_head -= SERIAL_XMIT_SIZE;
+		buf += block;
+	}
+	count = dport->xmit_cnt;
+	/* xmit buffer no longer empty? */
+	if (count)
+		INIT_COMPLETION(dport->xmit_empty);
+	mutex_unlock(&dport->xmit_lock);
+
+	if (total) {
+		/*
+		 * If the buffer is full, wake up the kthread, otherwise allow
+		 * some more time for the buffer to fill up a bit before waking
+		 * it.
+		 */
+		if (count == SERIAL_XMIT_SIZE) {
+			del_timer(&put_timer);
+			wake_up_interruptible(&dashtty_waitqueue);
+		} else {
+			mod_timer(&put_timer, jiffies + DA_TTY_PUT_DELAY);
+		}
+	}
+	return total;
+}
+
+static int dashtty_write_room(struct tty_struct *tty)
+{
+	struct dashtty_port *dport;
+	int channel;
+	int room;
+
+	channel = tty->index;
+	dport = &dashtty_ports[channel];
+
+	/* report the space in the xmit buffer */
+	mutex_lock(&dport->xmit_lock);
+	room = SERIAL_XMIT_SIZE - dport->xmit_cnt;
+	mutex_unlock(&dport->xmit_lock);
+
+	return room;
+}
+
+static int dashtty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct dashtty_port *dport;
+	int channel;
+	int chars;
+
+	channel = tty->index;
+	dport = &dashtty_ports[channel];
+
+	/* report the number of bytes in the xmit buffer */
+	mutex_lock(&dport->xmit_lock);
+	chars = dport->xmit_cnt;
+	mutex_unlock(&dport->xmit_lock);
+
+	return chars;
+}
+
+static const struct tty_operations dashtty_ops = {
+	.install		= dashtty_install,
+	.open			= dashtty_open,
+	.close			= dashtty_close,
+	.hangup			= dashtty_hangup,
+	.write			= dashtty_write,
+	.write_room		= dashtty_write_room,
+	.chars_in_buffer	= dashtty_chars_in_buffer,
+};
+
+static int __init dashtty_init(void)
+{
+	int ret;
+	int nport;
+	struct dashtty_port *dport;
+
+	if (!metag_da_enabled())
+		return -ENODEV;
+
+	channel_driver = tty_alloc_driver(NUM_TTY_CHANNELS,
+					  TTY_DRIVER_REAL_RAW);
+	if (IS_ERR(channel_driver))
+		return PTR_ERR(channel_driver);
+
+	channel_driver->driver_name = "metag_da";
+	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 |= CLOCAL;
+
+	tty_set_operations(channel_driver, &dashtty_ops);
+	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+		dport = &dashtty_ports[nport];
+		tty_port_init(&dport->port);
+		dport->port.ops = &dashtty_port_ops;
+		spin_lock_init(&dport->rx_lock);
+		mutex_init(&dport->xmit_lock);
+		/* the xmit buffer starts empty, i.e. completely written */
+		init_completion(&dport->xmit_empty);
+		complete(&dport->xmit_empty);
+	}
+
+	setup_timer(&put_timer, dashtty_put_timer, 0);
+
+	init_waitqueue_head(&dashtty_waitqueue);
+	dashtty_thread = kthread_create(put_data, NULL, "ttyDA");
+	if (IS_ERR(dashtty_thread)) {
+		pr_err("Couldn't create dashtty thread\n");
+		ret = PTR_ERR(dashtty_thread);
+		goto err_destroy_ports;
+	}
+	/*
+	 * Bind the writer thread to the boot CPU so it can't migrate.
+	 * DA channels are per-CPU and we want all channel I/O to be on a single
+	 * predictable CPU.
+	 */
+	kthread_bind(dashtty_thread, 0);
+	wake_up_process(dashtty_thread);
+
+	ret = tty_register_driver(channel_driver);
+
+	if (ret < 0) {
+		pr_err("Couldn't install dashtty driver: err %d\n",
+		       ret);
+		goto err_stop_kthread;
+	}
+
+	return 0;
+
+err_stop_kthread:
+	kthread_stop(dashtty_thread);
+err_destroy_ports:
+	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+		dport = &dashtty_ports[nport];
+		tty_port_destroy(&dport->port);
+	}
+	put_tty_driver(channel_driver);
+	return ret;
+}
+
+static void dashtty_exit(void)
+{
+	int nport;
+	struct dashtty_port *dport;
+
+	del_timer_sync(&put_timer);
+	kthread_stop(dashtty_thread);
+	del_timer_sync(&poll_timer);
+	tty_unregister_driver(channel_driver);
+	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
+		dport = &dashtty_ports[nport];
+		tty_port_destroy(&dport->port);
+	}
+	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] 15+ messages in thread

* [PATCH v4 43/43] fs: imgdafs: Add IMG DAFS filesystem for metag
  2013-01-29 14:15 ` James Hogan
@ 2013-01-29 14:15   ` James Hogan
  -1 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch
  Cc: Arnd Bergmann, James Hogan, Alexander Viro, Andrew Morton,
	Mauro Carvalho Chehab, Cesar Eduardo Barros, Joe Perches,
	David S. Miller, linux-fsdevel

Add the IMG Debug Adapter File System (DAFS) for metag, which uses
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: Andrew Morton <akpm@linux-foundation.org>
Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
Cc: Cesar Eduardo Barros <cesarb@cesarb.net>
Cc: Joe Perches <joe@perches.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: linux-fsdevel@vger.kernel.org
---
v4:
  * clean up fscall similarly to chancall in tty/metag_da

v2:
  * fixed fserrno problem (Al Viro)
  * renamed to imgdafs

 MAINTAINERS          |    1 +
 arch/metag/Kconfig   |    1 +
 fs/Kconfig           |    1 +
 fs/Makefile          |    1 +
 fs/imgdafs/Kconfig   |    6 +
 fs/imgdafs/Makefile  |    7 +
 fs/imgdafs/imgdafs.h |   80 +++++
 fs/imgdafs/inode.c   |  843 ++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 940 insertions(+), 0 deletions(-)
 create mode 100644 fs/imgdafs/Kconfig
 create mode 100644 fs/imgdafs/Makefile
 create mode 100644 fs/imgdafs/imgdafs.h
 create mode 100644 fs/imgdafs/inode.c

diff --git a/MAINTAINERS b/MAINTAINERS
index cb89fe3..6b02aea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5044,6 +5044,7 @@ F:	drivers/clocksource/metag_generic.c
 F:	drivers/irqchip/irq-metag.c
 F:	drivers/irqchip/irq-metag-ext.c
 F:	drivers/tty/metag_da.c
+F:	fs/imgdafs/
 
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index 9cba2f9..30adc78 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 780725a..6deefc1 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -211,6 +211,7 @@ source "fs/sysv/Kconfig"
 source "fs/ufs/Kconfig"
 source "fs/exofs/Kconfig"
 source "fs/f2fs/Kconfig"
+source "fs/imgdafs/Kconfig"
 
 endif # MISC_FILESYSTEMS
 
diff --git a/fs/Makefile b/fs/Makefile
index 9d53192..b070fca 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -127,3 +127,4 @@ obj-$(CONFIG_F2FS_FS)		+= f2fs/
 obj-y				+= exofs/ # Multiple modules
 obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
+obj-$(CONFIG_IMGDAFS_FS)	+= imgdafs/
diff --git a/fs/imgdafs/Kconfig b/fs/imgdafs/Kconfig
new file mode 100644
index 0000000..5293adb
--- /dev/null
+++ b/fs/imgdafs/Kconfig
@@ -0,0 +1,6 @@
+config IMGDAFS_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/imgdafs/Makefile b/fs/imgdafs/Makefile
new file mode 100644
index 0000000..169a3c6
--- /dev/null
+++ b/fs/imgdafs/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for DAfs the Debug Adapter filesystem.
+#
+
+obj-$(CONFIG_IMGDAFS_FS) += imgdafs.o
+
+imgdafs-objs := inode.o
diff --git a/fs/imgdafs/imgdafs.h b/fs/imgdafs/imgdafs.h
new file mode 100644
index 0000000..6c1da13
--- /dev/null
+++ b/fs/imgdafs/imgdafs.h
@@ -0,0 +1,80 @@
+#ifndef _IMGDAFS_H_
+#define _IMGDAFS_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/imgdafs/inode.c b/fs/imgdafs/inode.c
new file mode 100644
index 0000000..5808d90
--- /dev/null
+++ b/fs/imgdafs/inode.c
@@ -0,0 +1,843 @@
+/*
+ * 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 "imgdafs.h"
+
+static int fscall(int in_system_call, int in_arg1, int in_arg2, int in_arg3,
+		  int in_arg4, int in_arg5, int *fserrno)
+{
+	register int arg1            asm("D1Ar1") = in_arg1;
+	register int arg2            asm("D0Ar2") = in_arg2;
+	register int arg3            asm("D1Ar3") = in_arg3;
+	register int arg4            asm("D0Ar4") = in_arg4;
+	register int arg5            asm("D1Ar5") = in_arg5;
+	register int system_call     asm("D0Ar6") = in_system_call;
+	register int result          asm("D0Re0");
+	register int errno           asm("D1Re0");
+
+	asm volatile (
+		"MSETL	[A0StP++], %7,%5,%3\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");
+
+	if (fserrno)
+		*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;
+	int fserrno;
+	memset(p, 0, sizeof(*p));
+
+	if (fd >= 0) {
+		ret = fscall(DA_OP_FSTAT, fd, (int)p, 0, 0, 0, &fserrno);
+		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,
+			     &fserrno);
+		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, NULL);
+}
+
+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 *fserrno)
+{
+	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,
+		      fserrno);
+}
+
+static void close_dir(int handle)
+{
+	fscall(DA_OP_FINDCLOSE, handle, 0, 0, 0, 0, NULL);
+}
+
+static int read_dir(int handle, struct da_finddata *finddata)
+{
+	return fscall(DA_OP_FINDNEXT, handle, (int)finddata, 0, 0, 0, NULL);
+}
+
+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;
+	int fserrno;
+	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, &fserrno);
+	__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 cpu;
+
+	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_each_possible_cpu(cpu) {
+		u8 hwthread = cpu_2_hwthread_id[cpu];
+		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, NULL);
+	__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;
+	int fserrno;
+
+	n = fscall(DA_OP_PREAD, fd, (int)buf, len, (int)*offset, 0, &fserrno);
+
+	if (n < 0)
+		return -fserrno;
+
+	return n;
+}
+
+static int write_file(int fd, unsigned long long *offset, const char *buf,
+		      int len)
+{
+	int n;
+	int fserrno;
+
+	n = fscall(DA_OP_PWRITE, fd, (int)buf, len, (int)*offset, 0, &fserrno);
+
+	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;
+}
+
+static 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;
+}
+
+static 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);
+
+	i_uid_write(ino, st.st_uid);
+	i_gid_write(ino, 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 cpu;
+
+	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_each_possible_cpu(cpu) {
+		u8 hwthread = cpu_2_hwthread_id[cpu];
+		creat_flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread));
+	}
+
+	fd = fscall(DA_OP_OPEN, (int)name, creat_flags, damode, strlen(name),
+		    0, NULL);
+	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);
+}
+
+static 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;
+}
+
+static int dafs_unlink(struct inode *ino, struct dentry *dentry)
+{
+	char *file;
+	int err;
+	int fserrno;
+
+	file = dentry_name(dentry);
+	if (file == NULL)
+		return -ENOMEM;
+
+	err = fscall(DA_OP_UNLINK, (int)file, 0, 0, 0, 0, &fserrno);
+	__putname(file);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+static int do_mkdir(const char *file, int mode)
+{
+	int err;
+	int fserrno;
+
+	err = fscall(DA_OP_MKDIR, (int)file, mode, strlen(file), 0, 0,
+		     &fserrno);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+static 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;
+	int fserrno;
+
+	err = fscall(DA_OP_RMDIR, (int)file, strlen(file), 0, 0, 0, &fserrno);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+static 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;
+}
+
+static 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		= "imgdafs",
+	.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] 15+ messages in thread

* [PATCH v4 43/43] fs: imgdafs: Add IMG DAFS filesystem for metag
@ 2013-01-29 14:15   ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-01-29 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-arch
  Cc: Arnd Bergmann, James Hogan, Alexander Viro, Andrew Morton,
	Mauro Carvalho Chehab, Cesar Eduardo Barros, Joe Perches,
	David S. Miller, linux-fsdevel

Add the IMG Debug Adapter File System (DAFS) for metag, which uses
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: Andrew Morton <akpm@linux-foundation.org>
Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
Cc: Cesar Eduardo Barros <cesarb@cesarb.net>
Cc: Joe Perches <joe@perches.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: linux-fsdevel@vger.kernel.org
---
v4:
  * clean up fscall similarly to chancall in tty/metag_da

v2:
  * fixed fserrno problem (Al Viro)
  * renamed to imgdafs

 MAINTAINERS          |    1 +
 arch/metag/Kconfig   |    1 +
 fs/Kconfig           |    1 +
 fs/Makefile          |    1 +
 fs/imgdafs/Kconfig   |    6 +
 fs/imgdafs/Makefile  |    7 +
 fs/imgdafs/imgdafs.h |   80 +++++
 fs/imgdafs/inode.c   |  843 ++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 940 insertions(+), 0 deletions(-)
 create mode 100644 fs/imgdafs/Kconfig
 create mode 100644 fs/imgdafs/Makefile
 create mode 100644 fs/imgdafs/imgdafs.h
 create mode 100644 fs/imgdafs/inode.c

diff --git a/MAINTAINERS b/MAINTAINERS
index cb89fe3..6b02aea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5044,6 +5044,7 @@ F:	drivers/clocksource/metag_generic.c
 F:	drivers/irqchip/irq-metag.c
 F:	drivers/irqchip/irq-metag-ext.c
 F:	drivers/tty/metag_da.c
+F:	fs/imgdafs/
 
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
index 9cba2f9..30adc78 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 780725a..6deefc1 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -211,6 +211,7 @@ source "fs/sysv/Kconfig"
 source "fs/ufs/Kconfig"
 source "fs/exofs/Kconfig"
 source "fs/f2fs/Kconfig"
+source "fs/imgdafs/Kconfig"
 
 endif # MISC_FILESYSTEMS
 
diff --git a/fs/Makefile b/fs/Makefile
index 9d53192..b070fca 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -127,3 +127,4 @@ obj-$(CONFIG_F2FS_FS)		+= f2fs/
 obj-y				+= exofs/ # Multiple modules
 obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
+obj-$(CONFIG_IMGDAFS_FS)	+= imgdafs/
diff --git a/fs/imgdafs/Kconfig b/fs/imgdafs/Kconfig
new file mode 100644
index 0000000..5293adb
--- /dev/null
+++ b/fs/imgdafs/Kconfig
@@ -0,0 +1,6 @@
+config IMGDAFS_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/imgdafs/Makefile b/fs/imgdafs/Makefile
new file mode 100644
index 0000000..169a3c6
--- /dev/null
+++ b/fs/imgdafs/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for DAfs the Debug Adapter filesystem.
+#
+
+obj-$(CONFIG_IMGDAFS_FS) += imgdafs.o
+
+imgdafs-objs := inode.o
diff --git a/fs/imgdafs/imgdafs.h b/fs/imgdafs/imgdafs.h
new file mode 100644
index 0000000..6c1da13
--- /dev/null
+++ b/fs/imgdafs/imgdafs.h
@@ -0,0 +1,80 @@
+#ifndef _IMGDAFS_H_
+#define _IMGDAFS_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/imgdafs/inode.c b/fs/imgdafs/inode.c
new file mode 100644
index 0000000..5808d90
--- /dev/null
+++ b/fs/imgdafs/inode.c
@@ -0,0 +1,843 @@
+/*
+ * 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 "imgdafs.h"
+
+static int fscall(int in_system_call, int in_arg1, int in_arg2, int in_arg3,
+		  int in_arg4, int in_arg5, int *fserrno)
+{
+	register int arg1            asm("D1Ar1") = in_arg1;
+	register int arg2            asm("D0Ar2") = in_arg2;
+	register int arg3            asm("D1Ar3") = in_arg3;
+	register int arg4            asm("D0Ar4") = in_arg4;
+	register int arg5            asm("D1Ar5") = in_arg5;
+	register int system_call     asm("D0Ar6") = in_system_call;
+	register int result          asm("D0Re0");
+	register int errno           asm("D1Re0");
+
+	asm volatile (
+		"MSETL	[A0StP++], %7,%5,%3\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");
+
+	if (fserrno)
+		*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;
+	int fserrno;
+	memset(p, 0, sizeof(*p));
+
+	if (fd >= 0) {
+		ret = fscall(DA_OP_FSTAT, fd, (int)p, 0, 0, 0, &fserrno);
+		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,
+			     &fserrno);
+		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, NULL);
+}
+
+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 *fserrno)
+{
+	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,
+		      fserrno);
+}
+
+static void close_dir(int handle)
+{
+	fscall(DA_OP_FINDCLOSE, handle, 0, 0, 0, 0, NULL);
+}
+
+static int read_dir(int handle, struct da_finddata *finddata)
+{
+	return fscall(DA_OP_FINDNEXT, handle, (int)finddata, 0, 0, 0, NULL);
+}
+
+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;
+	int fserrno;
+	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, &fserrno);
+	__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 cpu;
+
+	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_each_possible_cpu(cpu) {
+		u8 hwthread = cpu_2_hwthread_id[cpu];
+		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, NULL);
+	__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;
+	int fserrno;
+
+	n = fscall(DA_OP_PREAD, fd, (int)buf, len, (int)*offset, 0, &fserrno);
+
+	if (n < 0)
+		return -fserrno;
+
+	return n;
+}
+
+static int write_file(int fd, unsigned long long *offset, const char *buf,
+		      int len)
+{
+	int n;
+	int fserrno;
+
+	n = fscall(DA_OP_PWRITE, fd, (int)buf, len, (int)*offset, 0, &fserrno);
+
+	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;
+}
+
+static 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;
+}
+
+static 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);
+
+	i_uid_write(ino, st.st_uid);
+	i_gid_write(ino, 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 cpu;
+
+	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_each_possible_cpu(cpu) {
+		u8 hwthread = cpu_2_hwthread_id[cpu];
+		creat_flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread));
+	}
+
+	fd = fscall(DA_OP_OPEN, (int)name, creat_flags, damode, strlen(name),
+		    0, NULL);
+	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);
+}
+
+static 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;
+}
+
+static int dafs_unlink(struct inode *ino, struct dentry *dentry)
+{
+	char *file;
+	int err;
+	int fserrno;
+
+	file = dentry_name(dentry);
+	if (file == NULL)
+		return -ENOMEM;
+
+	err = fscall(DA_OP_UNLINK, (int)file, 0, 0, 0, 0, &fserrno);
+	__putname(file);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+static int do_mkdir(const char *file, int mode)
+{
+	int err;
+	int fserrno;
+
+	err = fscall(DA_OP_MKDIR, (int)file, mode, strlen(file), 0, 0,
+		     &fserrno);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+static 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;
+	int fserrno;
+
+	err = fscall(DA_OP_RMDIR, (int)file, strlen(file), 0, 0, 0, &fserrno);
+	if (err)
+		return -fserrno;
+	return 0;
+}
+
+static 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;
+}
+
+static 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		= "imgdafs",
+	.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] 15+ messages in thread

* Re: [PATCH v4 43/43] fs: imgdafs: Add IMG DAFS filesystem for metag
  2013-01-29 14:15   ` James Hogan
@ 2013-02-06 15:02     ` James Hogan
  -1 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-02-06 15:02 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, linux-arch, Arnd Bergmann, Alexander Viro,
	Andrew Morton, Mauro Carvalho Chehab, Cesar Eduardo Barros,
	Joe Perches, David S. Miller, linux-fsdevel

Ping.

Any chance of an Ack from somebody on this patch? I'm currently holding
off including it in the arch/metag tree.

Thanks
James

On 29/01/13 14:15, James Hogan wrote:
> Add the IMG Debug Adapter File System (DAFS) for metag, which uses
> 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: Andrew Morton <akpm@linux-foundation.org>
> Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
> Cc: Cesar Eduardo Barros <cesarb@cesarb.net>
> Cc: Joe Perches <joe@perches.com>
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: linux-fsdevel@vger.kernel.org
> ---
> v4:
>   * clean up fscall similarly to chancall in tty/metag_da
> 
> v2:
>   * fixed fserrno problem (Al Viro)
>   * renamed to imgdafs
> 
>  MAINTAINERS          |    1 +
>  arch/metag/Kconfig   |    1 +
>  fs/Kconfig           |    1 +
>  fs/Makefile          |    1 +
>  fs/imgdafs/Kconfig   |    6 +
>  fs/imgdafs/Makefile  |    7 +
>  fs/imgdafs/imgdafs.h |   80 +++++
>  fs/imgdafs/inode.c   |  843 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  8 files changed, 940 insertions(+), 0 deletions(-)
>  create mode 100644 fs/imgdafs/Kconfig
>  create mode 100644 fs/imgdafs/Makefile
>  create mode 100644 fs/imgdafs/imgdafs.h
>  create mode 100644 fs/imgdafs/inode.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index cb89fe3..6b02aea 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5044,6 +5044,7 @@ F:	drivers/clocksource/metag_generic.c
>  F:	drivers/irqchip/irq-metag.c
>  F:	drivers/irqchip/irq-metag-ext.c
>  F:	drivers/tty/metag_da.c
> +F:	fs/imgdafs/
>  
>  MICROBLAZE ARCHITECTURE
>  M:	Michal Simek <monstr@monstr.eu>
> diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
> index 9cba2f9..30adc78 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 780725a..6deefc1 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -211,6 +211,7 @@ source "fs/sysv/Kconfig"
>  source "fs/ufs/Kconfig"
>  source "fs/exofs/Kconfig"
>  source "fs/f2fs/Kconfig"
> +source "fs/imgdafs/Kconfig"
>  
>  endif # MISC_FILESYSTEMS
>  
> diff --git a/fs/Makefile b/fs/Makefile
> index 9d53192..b070fca 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -127,3 +127,4 @@ obj-$(CONFIG_F2FS_FS)		+= f2fs/
>  obj-y				+= exofs/ # Multiple modules
>  obj-$(CONFIG_CEPH_FS)		+= ceph/
>  obj-$(CONFIG_PSTORE)		+= pstore/
> +obj-$(CONFIG_IMGDAFS_FS)	+= imgdafs/
> diff --git a/fs/imgdafs/Kconfig b/fs/imgdafs/Kconfig
> new file mode 100644
> index 0000000..5293adb
> --- /dev/null
> +++ b/fs/imgdafs/Kconfig
> @@ -0,0 +1,6 @@
> +config IMGDAFS_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/imgdafs/Makefile b/fs/imgdafs/Makefile
> new file mode 100644
> index 0000000..169a3c6
> --- /dev/null
> +++ b/fs/imgdafs/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for DAfs the Debug Adapter filesystem.
> +#
> +
> +obj-$(CONFIG_IMGDAFS_FS) += imgdafs.o
> +
> +imgdafs-objs := inode.o
> diff --git a/fs/imgdafs/imgdafs.h b/fs/imgdafs/imgdafs.h
> new file mode 100644
> index 0000000..6c1da13
> --- /dev/null
> +++ b/fs/imgdafs/imgdafs.h
> @@ -0,0 +1,80 @@
> +#ifndef _IMGDAFS_H_
> +#define _IMGDAFS_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/imgdafs/inode.c b/fs/imgdafs/inode.c
> new file mode 100644
> index 0000000..5808d90
> --- /dev/null
> +++ b/fs/imgdafs/inode.c
> @@ -0,0 +1,843 @@
> +/*
> + * 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 "imgdafs.h"
> +
> +static int fscall(int in_system_call, int in_arg1, int in_arg2, int in_arg3,
> +		  int in_arg4, int in_arg5, int *fserrno)
> +{
> +	register int arg1            asm("D1Ar1") = in_arg1;
> +	register int arg2            asm("D0Ar2") = in_arg2;
> +	register int arg3            asm("D1Ar3") = in_arg3;
> +	register int arg4            asm("D0Ar4") = in_arg4;
> +	register int arg5            asm("D1Ar5") = in_arg5;
> +	register int system_call     asm("D0Ar6") = in_system_call;
> +	register int result          asm("D0Re0");
> +	register int errno           asm("D1Re0");
> +
> +	asm volatile (
> +		"MSETL	[A0StP++], %7,%5,%3\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");
> +
> +	if (fserrno)
> +		*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;
> +	int fserrno;
> +	memset(p, 0, sizeof(*p));
> +
> +	if (fd >= 0) {
> +		ret = fscall(DA_OP_FSTAT, fd, (int)p, 0, 0, 0, &fserrno);
> +		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,
> +			     &fserrno);
> +		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, NULL);
> +}
> +
> +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 *fserrno)
> +{
> +	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,
> +		      fserrno);
> +}
> +
> +static void close_dir(int handle)
> +{
> +	fscall(DA_OP_FINDCLOSE, handle, 0, 0, 0, 0, NULL);
> +}
> +
> +static int read_dir(int handle, struct da_finddata *finddata)
> +{
> +	return fscall(DA_OP_FINDNEXT, handle, (int)finddata, 0, 0, 0, NULL);
> +}
> +
> +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;
> +	int fserrno;
> +	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, &fserrno);
> +	__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 cpu;
> +
> +	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_each_possible_cpu(cpu) {
> +		u8 hwthread = cpu_2_hwthread_id[cpu];
> +		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, NULL);
> +	__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;
> +	int fserrno;
> +
> +	n = fscall(DA_OP_PREAD, fd, (int)buf, len, (int)*offset, 0, &fserrno);
> +
> +	if (n < 0)
> +		return -fserrno;
> +
> +	return n;
> +}
> +
> +static int write_file(int fd, unsigned long long *offset, const char *buf,
> +		      int len)
> +{
> +	int n;
> +	int fserrno;
> +
> +	n = fscall(DA_OP_PWRITE, fd, (int)buf, len, (int)*offset, 0, &fserrno);
> +
> +	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;
> +}
> +
> +static 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;
> +}
> +
> +static 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);
> +
> +	i_uid_write(ino, st.st_uid);
> +	i_gid_write(ino, 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 cpu;
> +
> +	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_each_possible_cpu(cpu) {
> +		u8 hwthread = cpu_2_hwthread_id[cpu];
> +		creat_flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread));
> +	}
> +
> +	fd = fscall(DA_OP_OPEN, (int)name, creat_flags, damode, strlen(name),
> +		    0, NULL);
> +	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);
> +}
> +
> +static 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;
> +}
> +
> +static int dafs_unlink(struct inode *ino, struct dentry *dentry)
> +{
> +	char *file;
> +	int err;
> +	int fserrno;
> +
> +	file = dentry_name(dentry);
> +	if (file == NULL)
> +		return -ENOMEM;
> +
> +	err = fscall(DA_OP_UNLINK, (int)file, 0, 0, 0, 0, &fserrno);
> +	__putname(file);
> +	if (err)
> +		return -fserrno;
> +	return 0;
> +}
> +
> +static int do_mkdir(const char *file, int mode)
> +{
> +	int err;
> +	int fserrno;
> +
> +	err = fscall(DA_OP_MKDIR, (int)file, mode, strlen(file), 0, 0,
> +		     &fserrno);
> +	if (err)
> +		return -fserrno;
> +	return 0;
> +}
> +
> +static 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;
> +	int fserrno;
> +
> +	err = fscall(DA_OP_RMDIR, (int)file, strlen(file), 0, 0, 0, &fserrno);
> +	if (err)
> +		return -fserrno;
> +	return 0;
> +}
> +
> +static 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;
> +}
> +
> +static 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		= "imgdafs",
> +	.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");
> 


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

* Re: [PATCH v4 43/43] fs: imgdafs: Add IMG DAFS filesystem for metag
@ 2013-02-06 15:02     ` James Hogan
  0 siblings, 0 replies; 15+ messages in thread
From: James Hogan @ 2013-02-06 15:02 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, linux-arch, Arnd Bergmann, Alexander Viro,
	Andrew Morton, Mauro Carvalho Chehab, Cesar Eduardo Barros,
	Joe Perches, David S. Miller, linux-fsdevel

Ping.

Any chance of an Ack from somebody on this patch? I'm currently holding
off including it in the arch/metag tree.

Thanks
James

On 29/01/13 14:15, James Hogan wrote:
> Add the IMG Debug Adapter File System (DAFS) for metag, which uses
> 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: Andrew Morton <akpm@linux-foundation.org>
> Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
> Cc: Cesar Eduardo Barros <cesarb@cesarb.net>
> Cc: Joe Perches <joe@perches.com>
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: linux-fsdevel@vger.kernel.org
> ---
> v4:
>   * clean up fscall similarly to chancall in tty/metag_da
> 
> v2:
>   * fixed fserrno problem (Al Viro)
>   * renamed to imgdafs
> 
>  MAINTAINERS          |    1 +
>  arch/metag/Kconfig   |    1 +
>  fs/Kconfig           |    1 +
>  fs/Makefile          |    1 +
>  fs/imgdafs/Kconfig   |    6 +
>  fs/imgdafs/Makefile  |    7 +
>  fs/imgdafs/imgdafs.h |   80 +++++
>  fs/imgdafs/inode.c   |  843 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  8 files changed, 940 insertions(+), 0 deletions(-)
>  create mode 100644 fs/imgdafs/Kconfig
>  create mode 100644 fs/imgdafs/Makefile
>  create mode 100644 fs/imgdafs/imgdafs.h
>  create mode 100644 fs/imgdafs/inode.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index cb89fe3..6b02aea 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5044,6 +5044,7 @@ F:	drivers/clocksource/metag_generic.c
>  F:	drivers/irqchip/irq-metag.c
>  F:	drivers/irqchip/irq-metag-ext.c
>  F:	drivers/tty/metag_da.c
> +F:	fs/imgdafs/
>  
>  MICROBLAZE ARCHITECTURE
>  M:	Michal Simek <monstr@monstr.eu>
> diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
> index 9cba2f9..30adc78 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 780725a..6deefc1 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -211,6 +211,7 @@ source "fs/sysv/Kconfig"
>  source "fs/ufs/Kconfig"
>  source "fs/exofs/Kconfig"
>  source "fs/f2fs/Kconfig"
> +source "fs/imgdafs/Kconfig"
>  
>  endif # MISC_FILESYSTEMS
>  
> diff --git a/fs/Makefile b/fs/Makefile
> index 9d53192..b070fca 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -127,3 +127,4 @@ obj-$(CONFIG_F2FS_FS)		+= f2fs/
>  obj-y				+= exofs/ # Multiple modules
>  obj-$(CONFIG_CEPH_FS)		+= ceph/
>  obj-$(CONFIG_PSTORE)		+= pstore/
> +obj-$(CONFIG_IMGDAFS_FS)	+= imgdafs/
> diff --git a/fs/imgdafs/Kconfig b/fs/imgdafs/Kconfig
> new file mode 100644
> index 0000000..5293adb
> --- /dev/null
> +++ b/fs/imgdafs/Kconfig
> @@ -0,0 +1,6 @@
> +config IMGDAFS_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/imgdafs/Makefile b/fs/imgdafs/Makefile
> new file mode 100644
> index 0000000..169a3c6
> --- /dev/null
> +++ b/fs/imgdafs/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for DAfs the Debug Adapter filesystem.
> +#
> +
> +obj-$(CONFIG_IMGDAFS_FS) += imgdafs.o
> +
> +imgdafs-objs := inode.o
> diff --git a/fs/imgdafs/imgdafs.h b/fs/imgdafs/imgdafs.h
> new file mode 100644
> index 0000000..6c1da13
> --- /dev/null
> +++ b/fs/imgdafs/imgdafs.h
> @@ -0,0 +1,80 @@
> +#ifndef _IMGDAFS_H_
> +#define _IMGDAFS_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/imgdafs/inode.c b/fs/imgdafs/inode.c
> new file mode 100644
> index 0000000..5808d90
> --- /dev/null
> +++ b/fs/imgdafs/inode.c
> @@ -0,0 +1,843 @@
> +/*
> + * 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 "imgdafs.h"
> +
> +static int fscall(int in_system_call, int in_arg1, int in_arg2, int in_arg3,
> +		  int in_arg4, int in_arg5, int *fserrno)
> +{
> +	register int arg1            asm("D1Ar1") = in_arg1;
> +	register int arg2            asm("D0Ar2") = in_arg2;
> +	register int arg3            asm("D1Ar3") = in_arg3;
> +	register int arg4            asm("D0Ar4") = in_arg4;
> +	register int arg5            asm("D1Ar5") = in_arg5;
> +	register int system_call     asm("D0Ar6") = in_system_call;
> +	register int result          asm("D0Re0");
> +	register int errno           asm("D1Re0");
> +
> +	asm volatile (
> +		"MSETL	[A0StP++], %7,%5,%3\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");
> +
> +	if (fserrno)
> +		*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;
> +	int fserrno;
> +	memset(p, 0, sizeof(*p));
> +
> +	if (fd >= 0) {
> +		ret = fscall(DA_OP_FSTAT, fd, (int)p, 0, 0, 0, &fserrno);
> +		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,
> +			     &fserrno);
> +		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, NULL);
> +}
> +
> +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 *fserrno)
> +{
> +	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,
> +		      fserrno);
> +}
> +
> +static void close_dir(int handle)
> +{
> +	fscall(DA_OP_FINDCLOSE, handle, 0, 0, 0, 0, NULL);
> +}
> +
> +static int read_dir(int handle, struct da_finddata *finddata)
> +{
> +	return fscall(DA_OP_FINDNEXT, handle, (int)finddata, 0, 0, 0, NULL);
> +}
> +
> +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;
> +	int fserrno;
> +	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, &fserrno);
> +	__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 cpu;
> +
> +	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_each_possible_cpu(cpu) {
> +		u8 hwthread = cpu_2_hwthread_id[cpu];
> +		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, NULL);
> +	__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;
> +	int fserrno;
> +
> +	n = fscall(DA_OP_PREAD, fd, (int)buf, len, (int)*offset, 0, &fserrno);
> +
> +	if (n < 0)
> +		return -fserrno;
> +
> +	return n;
> +}
> +
> +static int write_file(int fd, unsigned long long *offset, const char *buf,
> +		      int len)
> +{
> +	int n;
> +	int fserrno;
> +
> +	n = fscall(DA_OP_PWRITE, fd, (int)buf, len, (int)*offset, 0, &fserrno);
> +
> +	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;
> +}
> +
> +static 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;
> +}
> +
> +static 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);
> +
> +	i_uid_write(ino, st.st_uid);
> +	i_gid_write(ino, 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 cpu;
> +
> +	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_each_possible_cpu(cpu) {
> +		u8 hwthread = cpu_2_hwthread_id[cpu];
> +		creat_flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread));
> +	}
> +
> +	fd = fscall(DA_OP_OPEN, (int)name, creat_flags, damode, strlen(name),
> +		    0, NULL);
> +	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);
> +}
> +
> +static 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;
> +}
> +
> +static int dafs_unlink(struct inode *ino, struct dentry *dentry)
> +{
> +	char *file;
> +	int err;
> +	int fserrno;
> +
> +	file = dentry_name(dentry);
> +	if (file == NULL)
> +		return -ENOMEM;
> +
> +	err = fscall(DA_OP_UNLINK, (int)file, 0, 0, 0, 0, &fserrno);
> +	__putname(file);
> +	if (err)
> +		return -fserrno;
> +	return 0;
> +}
> +
> +static int do_mkdir(const char *file, int mode)
> +{
> +	int err;
> +	int fserrno;
> +
> +	err = fscall(DA_OP_MKDIR, (int)file, mode, strlen(file), 0, 0,
> +		     &fserrno);
> +	if (err)
> +		return -fserrno;
> +	return 0;
> +}
> +
> +static 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;
> +	int fserrno;
> +
> +	err = fscall(DA_OP_RMDIR, (int)file, strlen(file), 0, 0, 0, &fserrno);
> +	if (err)
> +		return -fserrno;
> +	return 0;
> +}
> +
> +static 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;
> +}
> +
> +static 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		= "imgdafs",
> +	.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");
> 

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

end of thread, other threads:[~2013-02-06 15:02 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-29 14:15 [PATCH v4 00/43] Meta Linux Kernel Port James Hogan
2013-01-29 14:15 ` James Hogan
2013-01-29 14:15 ` [PATCH v4 19/43] metag: Device tree James Hogan
2013-01-29 14:15   ` James Hogan
2013-01-29 14:15   ` James Hogan
2013-01-29 14:15 ` [PATCH v4 21/43] metag: Time keeping James Hogan
2013-01-29 14:15   ` James Hogan
2013-01-29 14:15 ` [PATCH v4 26/43] metag: Scheduling/Process management James Hogan
2013-01-29 14:15   ` James Hogan
2013-01-29 14:15 ` [PATCH v4 42/43] tty/metag_da: Add metag DA TTY driver James Hogan
2013-01-29 14:15   ` James Hogan
2013-01-29 14:15 ` [PATCH v4 43/43] fs: imgdafs: Add IMG DAFS filesystem for metag James Hogan
2013-01-29 14:15   ` James Hogan
2013-02-06 15:02   ` James Hogan
2013-02-06 15:02     ` 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.